public Task <SocketReceiveResult> ReceiveAsync() { ThrowIfDisposed(); var tcs = new TaskCompletionSource <SocketReceiveResult>(); EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; #if NETSTANDARD1_6 _Socket.ReceiveFromAsync(new ArraySegment <Byte>(state.Buffer), SocketFlags.None, state.RemoteEndPoint) .ContinueWith((task, asyncState) => { if (task.Status != TaskStatus.Faulted) { var receiveState = asyncState as AsyncReceiveState; receiveState.RemoteEndPoint = task.Result.RemoteEndPoint; ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress); } }, state); #else _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); #endif return(tcs.Task); }
public Task <SocketReceiveResult> ReceiveAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); var tcs = new TaskCompletionSource <SocketReceiveResult>(); EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; cancellationToken.Register(() => tcs.TrySetCanceled()); _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; _currentReceiveTaskCompletionSource = tcs; try { var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); if (!willRaiseEvent) { _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); } } catch (Exception ex) { tcs.TrySetException(ex); } return(tcs.Task); }
public System.Threading.Tasks.Task <ReceivedUdpData> ReceiveAsync() { ThrowIfDisposed(); var tcs = new TaskCompletionSource <ReceivedUdpData>(); System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; #if NETSTANDARD1_3 _Socket.ReceiveFromAsync(new System.ArraySegment <Byte>(state.Buffer), System.Net.Sockets.SocketFlags.None, state.EndPoint) .ContinueWith((task, asyncState) => { if (task.Status != TaskStatus.Faulted) { var receiveState = asyncState as AsyncReceiveState; receiveState.EndPoint = task.Result.RemoteEndPoint; ProcessResponse(receiveState, () => task.Result.ReceivedBytes); } }, state); #else _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback((result) => ProcessResponse(state, () => state.Socket.EndReceiveFrom(result, ref state.EndPoint))), state); #endif return(tcs.Task); }
// handles the request on a separate ThreadPool thread. private void HandleRequestAsync(AsyncReceiveState asyncState) { // TODO: first try pull the request from the request cache, otherwise parse it now // convert the raw request bytes/string into an HttpRequest object for ease of use string reqString = Text.Encoding.ASCII.GetString(asyncState.Buffer).Trim('\0'); Request req; // pull the request object from the request cache, or create it now if (RequestCache.ContainsKey(reqString)) { req = RequestCache[reqString]; } else { req = new Request(reqString, this, asyncState.Socket, asyncState.Site); RequestCache.TryAdd(reqString, req); } // first try to serve the response from the output cache bool servedFromCache = false; //bool cacheAllowed = true; if (EnableOutputCache && req.CacheAllowed) { if (OutputCache.ContainsKey((req.AbsPath + req.QueryString))) { Response res = OutputCache[(req.AbsPath + req.QueryString)]; SendResponse(req, res, asyncState.Socket); servedFromCache = true; DebugMessage(("Serving resource from cache: " + (req.AbsPath + ".")), DebugMessageType.UsageMessage, "HandleRequestAsync"); } } // response couldn't be served from cache, handle the request according to the site role if (servedFromCache) { // if the current site is a reverse proxy/load balancer with upstream servers defined, forward the request to another server, // otherwise handle the request by this server like normal if (asyncState.Site.Role == "Standard") { // raise an event to handle the request/response cycle (this can be overridden during implementation to allow for custom handling) HandleRequest(new Tuple <object, object>(req, asyncState.Socket), null); } else if (asyncState.Site.Role == "Load Balancer") { // parse the upstream servers and select one using the defined algorithm string[] upstreams = asyncState.Site.Upstream.Split(','); Random r = new Random(); int i = r.Next(0, upstreams.Length); // forward the request to the selected upstream server ProxyRequest(new Tuple <object, object, object>(req, upstreams[i], asyncState.Socket), null); //??? } } }
private static void ProcessResponse(AsyncReceiveState state, Func <int> receiveData) { try { var bytesRead = receiveData(); var ipEndPoint = state.EndPoint as IPEndPoint; state.TaskCompletionSource.SetResult( new ReceivedUdpData() { Buffer = state.Buffer, ReceivedBytes = bytesRead, ReceivedFrom = new UdpEndPoint() { IPAddress = ipEndPoint.Address.ToString(), Port = ipEndPoint.Port } } ); } catch (ObjectDisposedException) { state.TaskCompletionSource.SetCanceled(); } catch (SocketException se) { if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown) { state.TaskCompletionSource.SetException(se); } else { state.TaskCompletionSource.SetCanceled(); } } #if NETSTANDARD // Unrecoverable exceptions should not be getting caught and will be dealt with on a broad level by a high-level catch-all handler // https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/breaking-change-rules.md#exceptions #else catch (StackOverflowException) // Handle this manually as we may not be able to call a sub method to check exception type etc. { throw; } #endif catch (Exception ex) { if (ex.IsCritical()) //Critical exceptions that indicate memory corruption etc. shouldn't be caught. { throw; } state.TaskCompletionSource.SetException(ex); } }
private void AsyncClientConnected(IAsyncResult ar) { // get the async state object returned by the callback AsyncSendState asyncState = ((AsyncSendState)(ar.AsyncState)); object state = asyncState.State; // end the async connection request so we can check if we are connected to the server bool connectSuccessful = false; try { // call the EndConnect method which will succeed or throw an error depending on the result of the connection asyncState.Socket.EndConnect(ar); // at this point, the EndConnect succeeded and we are connected to the server! handle the success outside this Try block. connectSuccessful = true; ConnectSucceeded(state, null); } catch (Exception ex) { // at this point, the EndConnect failed and we are NOT connected to the server! DebugMessage("Could not connect to the server.", DebugMessageType.ErrorMessage, "Connect", ex.Message); ConnectFailed(state, null); } // if the client has connected, proceed if (connectSuccessful) { // start waiting for messages from the server AsyncReceiveState receiveState = new AsyncReceiveState(ReceiveBufferSize, state); receiveState.Socket = asyncState.Socket; receiveState.Socket.BeginReceive(receiveState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(DataReceived), receiveState); // make a request to the server AsyncSendState sendState = new AsyncSendState(asyncState.Socket, SendBufferSize, state); // if the path is a directory, ensure it has a trailing / // If IO.Path.GetExtension(_request) = "" Then // If _request.EndsWith("/") = False Then // _request &= "/" // End If // End If // construct the GET request string and byte array: // NOTE: node.js requires two vbCrLf terminator where other servers only require one. IIS 7.5 requires HTTP/1.1 and Host // header or will not return headers with the response. string reqString = ""; byte[] reqBytes = null; reqString = ("GET " + (_req.Path + (" HTTP/1.1" + ('\n' + ("Host: " + (_req.Host + ('\n' + '\n'))))))); reqBytes = System.Text.Encoding.ASCII.GetBytes(reqString); // send the reqBytes data to the server LogMessage(reqString, null); sendState.Socket.BeginSend(reqBytes, 0, reqBytes.Length, SocketFlags.None, new AsyncCallback(DataSent), sendState); } }
public System.Threading.Tasks.Task<ReceivedUdpData> ReceiveAsync() { ThrowIfDisposed(); var tcs = new TaskCompletionSource<ReceivedUdpData>(); System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback(this.ProcessResponse), state); return tcs.Task; }
public System.Threading.Tasks.Task <ReceivedUdpData> ReceiveAsync() { ThrowIfDisposed(); var tcs = new TaskCompletionSource <ReceivedUdpData>(); System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback(this.ProcessResponse), state); return(tcs.Task); }
public System.Threading.Tasks.Task <ReceivedUdpData> ReceiveAsync() { ThrowIfDisposed(); var tcs = new TaskCompletionSource <ReceivedUdpData>(); System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(GetDefaultIpAddress(_Socket), 0); var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; #if NETSTANDARD1_3 _Socket.ReceiveFromAsync(new System.ArraySegment <Byte>(state.Buffer), System.Net.Sockets.SocketFlags.None, state.EndPoint) .ContinueWith((task, asyncState) => { if (this.IsDisposed) { return; } try { if (task.Status != TaskStatus.Faulted) { var receiveState = asyncState as AsyncReceiveState; receiveState.EndPoint = task.Result.RemoteEndPoint; ProcessResponse(receiveState, () => task.Result.ReceivedBytes); } } catch (ObjectDisposedException) { if (!this.IsDisposed) { throw; } } //Only rethrow disposed exceptions if we're NOT disposed, because then they are unexpected. }, state); #else try { _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback((result) => ProcessResponse(state, () => state.Socket.EndReceiveFrom(result, ref state.EndPoint))), state); } catch (ObjectDisposedException) { if (!this.IsDisposed) { throw; } } //Only rethrow disposed exceptions if we're NOT disposed, because then they are unexpected. #endif return(tcs.Task); }
// '' <summary> // '' This callback fires when the client socket receives data (an HTTP response) asynchronously from the server. // '' </summary> // '' NOTE: // '' Since HTTP is by nature a stream, a response will be sent by the server which is broken down into pieces per the // '' server's configured SendBufferSize, so we must continue issuing BeginReceive's on the socket until the end of the stream. // '' We can properly detect the end of stream per the HTTP spec which states that the Content-Length header should be used to stop // '' issuiing BeginReceive's when the total bytes received equals the Header + Content length, or in the case of a // '' "Transfer-Encoding: chunked" header we look for a null character which signals termination of the chunked stream. // '' <param name="ar"></param> // '' <remarks></remarks> private void DataReceived(IAsyncResult ar) { // get the async state object returned by the callback AsyncReceiveState asyncState = ((AsyncReceiveState)(ar.AsyncState)); string responseChunk = System.Text.Encoding.ASCII.GetString(asyncState.Buffer).TrimEnd(Convert.ToChar(0)); string responseString = (asyncState.Packet + responseChunk); // if we haven't determined the Content-Length yet, try doing so now by attempting to extract it from the responseChunk: // TODO: this halts on an error when we try a random URL. // TODO: we need to handle the various transfer types here...check for chunked encoding and parse the size etc... if (asyncState.ReceiveSize == 0) { string contentLength = ""; //string transferEncoding = ""; contentLength = responseChunk.SubstringEx("Content-Length: ", '\n'); asyncState.ReceiveSize = int.Parse(contentLength); } // if we haven't determined the Content offset yet, try doing so now. content is located after the header and two newlines (crlf) which is 4 bytes. if (asyncState.ContentOffset == 0) { int contentOffset; contentOffset = (responseChunk.IndexOf(Environment.NewLine + Environment.NewLine) + 4); asyncState.ContentOffset = contentOffset; } // add the responseChunk's length to the total received bytes count: asyncState.TotalBytesReceived = (asyncState.TotalBytesReceived + responseChunk.Length); // if we haven't received all the bytes yet, issue another BeginReceive, otherwise we have all the data so we handle the response if (asyncState.TotalBytesReceived - asyncState.ContentOffset < asyncState.ReceiveSize) { AsyncReceiveState receiveState = new AsyncReceiveState(ReceiveBufferSize, asyncState.State); receiveState.Socket = asyncState.Socket; receiveState.Packet = responseString; receiveState.ReceiveSize = asyncState.ReceiveSize; receiveState.TotalBytesReceived = asyncState.TotalBytesReceived; receiveState.Socket.BeginReceive(receiveState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(DataReceived), receiveState); } else { HandleResponse(responseString, (EventArgs)asyncState.State); } }
// sends the http response to the client private void SendResponse(Request req, Response res, Socket client) { // convert the response into bytes and package it in an async object for callback purposes: // Dim responseBytes() As Byte = res.BuildResponseBytes AsyncSendState sendState = new AsyncSendState(client, SendBufferSize, null); sendState.BytesToSend = res.ResponseBytes; sendState.Tag = req.AbsPath; // start sending the response to the client in an async fashion: try { client.BeginSend(res.ResponseBytes, 0, res.ResponseBytes.Length, SocketFlags.None, SendResponseAsync, sendState); } catch (Exception ex) { DebugMessage("Unhandled exception in SendResponse when trying to send data to the client.", DebugMessageType.UsageMessage, "SendResponse", ex.Message); } // determine whether or not to continue receiving more data from the client: // TODO: tidy this up a bit instead of setting a property to true then checking it right afterwards... // IMPORTANT: for keep-alive connections we should make a final receive call to the client and if the client does not send a Connection: keep-alive header then we know to disconnect if (res.Headers.ContainsKey("Connection") && res.Headers["Connection"].ToString().ToLower() == "keep-alive") { sendState.Persistent = true; } // start receiving more data from the client in an async fashion if (sendState.Persistent) { AsyncReceiveState receiveState = new AsyncReceiveState(ReceiveBufferSize, null); receiveState.Site = req.Site; receiveState.Socket = client; try { receiveState.Socket.BeginReceive(receiveState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(RequestReceivedAsync), receiveState); } catch (Exception ex) { DebugMessage(("SendResponse encountered an exception when trying to BeginReceive on the client socket. " + ex.Message), DebugMessageType.ErrorMessage, "ClientRequest", ex.Message); } } }
// '' <summary> // '' Accepts the client request, builds the request/response objects and passes them to the event handler where the response // '' object will be finalized and sent back to the client. // '' </summary> // '' <param name="ar"></param> // '' <remarks></remarks> private void RequestReceivedAsync(IAsyncResult ar) { // get the async state object: AsyncReceiveState asyncState = ((AsyncReceiveState)(ar.AsyncState)); int numBytesReceived = 0; try { // call EndReceive which will give us the number of bytes received numBytesReceived = asyncState.Socket.EndReceive(ar); } catch (SocketException ex) { // if we get a ConnectionReset exception, it could indicate that the client has disconnected if (ex.SocketErrorCode == SocketError.ConnectionReset) { DebugMessage("EndReceive on the client socket failed because the client has disconnected.", DebugMessageType.UsageMessage, "RequestReceivedAsync", ex.Message); // update the connected client count in a thread safe way Threading.Interlocked.Decrement(ref ConnectedClients); // RaiseEvent ClientDisconnected(asyncState.Socket) return; } } // if we get numBytesReceived equal to zero, it could indicate that the client has disconnected // TODO: does this actually disconnect the client though, or just assume it was? if (numBytesReceived == 0) { // update the connected client count in a thread safe way Threading.Interlocked.Decrement(ref ConnectedClients); // RaiseEvent ClientDisconnected(asyncState.Socket) return; } // if we've reached this point, we were able to parse the IAsyncResult which contains our raw request bytes, so proceed // to handle it on a separate ThreadPool thread; it is important that we free up the I/O completion port being // used for this async operation as soon as possible, therefore we don't even attempt to parse the requestBytes at // this point and just immediately pass the raw request bytes to a ThreadPool thread for further processing Threading.ThreadPool.QueueUserWorkItem(new Threading.WaitCallback((o) => { HandleRequestAsync((AsyncReceiveState)o); }), asyncState); }
private static void ProcessResponse(AsyncReceiveState state, Func <int> receiveData, IpAddressInfo localIpAddress) { try { var bytesRead = receiveData(); var ipEndPoint = state.RemoteEndPoint as IPEndPoint; state.TaskCompletionSource.SetResult( new SocketReceiveResult { Buffer = state.Buffer, ReceivedBytes = bytesRead, RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), LocalIPAddress = localIpAddress } ); } catch (ObjectDisposedException) { state.TaskCompletionSource.SetCanceled(); } catch (SocketException se) { if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown) { state.TaskCompletionSource.SetException(se); } else { state.TaskCompletionSource.SetCanceled(); } } catch (Exception ex) { state.TaskCompletionSource.SetException(ex); } }
// '' <summary> // '' Accepts the client that is attempting to connect, allows another client to connect (whenever that may be), and finally begins receiving data from the client that just connected. // '' </summary> // '' <param name="ar"></param> // '' <remarks></remarks> private void ClientConnectedAsync(IAsyncResult ar) { // get the async state object from the async BeginAccept method, which contains the server's listening socket Site s = ((Site)(ar.AsyncState)); Socket clientSocket = null; try { // accept the client connection, giving us the client socket to work with: clientSocket = s.Socket.EndAccept(ar); // update the connected client count in a thread safe way Threading.Interlocked.Increment(ref ConnectedClients); // RaiseEvent ClientConnected(clientSocket) // DebugMessage("Client connected. Total connections: " & _connections, DebugMessageType.UsageMessage, "ClientConnectedAsync") // begin accepting another client connection: s.Socket.BeginAccept(0, new AsyncCallback(ClientConnectedAsync), s); } catch (ObjectDisposedException ex1) { // if we get an ObjectDisposedException it that means the server socket terminated while this async method was still active DebugMessage("The server was closed before the async method could complete.", DebugMessageType.WarningMessage, "ClientConnectedAsync", ex1.Message); return; } catch (Exception ex2) { DebugMessage("An unhandled exception occurred in ClientConnectedAsync.", DebugMessageType.ErrorMessage, "ClientConnectedAsync", ex2.Message); return; } // begin receiving data (http requests) from the client socket: AsyncReceiveState asyncState = new AsyncReceiveState(ReceiveBufferSize, null); asyncState.Site = s; asyncState.Socket = clientSocket; asyncState.Socket.BeginReceive(asyncState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(RequestReceivedAsync), asyncState); }