// This method is called by I/O Completed() when an asynchronous send completes. // If all of the data has been sent, then this method calls StartReceive //to start another receive op on the socket to read any additional // data sent from the client. If all of the data has NOT been sent, then it //calls StartSend to send more data. private void ProcessSend(SocketAsyncEventArgs receiveSendEventArgs) { DataHolderToken token = (DataHolderToken)receiveSendEventArgs.UserToken; if (receiveSendEventArgs.SocketError == SocketError.Success) { token.sendBytesRemainingCount = token.sendBytesRemainingCount - receiveSendEventArgs.BytesTransferred; if (token.sendBytesRemainingCount == 0) { // If we are within this if-statement, then all the bytes in // the message have been sent. token.Reset(); CloseClientSocket(receiveSendEventArgs); } else { // If some of the bytes in the message have NOT been sent, // then we will need to post another send operation, after we store // a count of how many bytes that we sent in this send op. token.bytesSentAlreadyCount += receiveSendEventArgs.BytesTransferred; // So let's loop back to StartSend(). StartSend(receiveSendEventArgs); } } else { // We'll just close the socket if there was a // socket error when receiving data from the client. token.Reset(); CloseClientSocket(receiveSendEventArgs); } }
/// <summary> /// Initializes the server by preallocating reusable buffers and context objects. These objects do not /// need to be preallocated or reused, by is done this way to illustrate how the API can easily be used /// to create reusable objects to increase server performance. /// </summary> public void InitServer() { // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds // against memory fragmentation bufferManager.InitBuffer(); //Allocate the pool of accept operation EventArgs objects //which do not need a buffer for (int i = 0; i < maxSimultaneousAcceptOps; i++) { SocketAsyncEventArgs acceptEventArgs = new SocketAsyncEventArgs(); acceptEventArgs.Completed += new EventHandler <SocketAsyncEventArgs>(AcceptEventArg_Completed); poolOfAcceptEventArgs.Push(acceptEventArgs); } //Allocate the pool of receive/send operation EventArgs objects //which DO need a buffer for (int i = 0; i < numberOfEventArgsForRecSend; i++) { SocketAsyncEventArgs sendReceiveEventArgs = new SocketAsyncEventArgs(); bufferManager.SetBuffer(sendReceiveEventArgs); sendReceiveEventArgs.Completed += new EventHandler <SocketAsyncEventArgs>(IO_Completed); DataHolderToken token = new DataHolderToken(sendReceiveEventArgs.Offset, receiveBufferSize); sendReceiveEventArgs.UserToken = token; poolOfRecSendEventArgs.Push(sendReceiveEventArgs); } }
string[] getCommandsFromArgs(SocketAsyncEventArgs e) { DataHolderToken token = (DataHolderToken)e.UserToken; string[] req = token.httpRequest.Split(new char[] { '?' }, 2); //Strip off "?" string[] cmd_stack = req[0].Split(new char[] { '/' }); string[] command = cmd_stack[0].Split(new char[] { ' ' }, 2); return(command); }
/// <summary> /// This method is invoked when an asynchronous receive operation completes. If the /// remote host closed the connection, then the socket is closed. If data was received then /// the data is echoed back to the client. /// </summary> private void ProcessReceive(SocketAsyncEventArgs receiveSendEventArgs) { DataHolderToken token = (DataHolderToken)receiveSendEventArgs.UserToken; //If there was a socket error, close the connection if (receiveSendEventArgs.SocketError != SocketError.Success) { token.Reset(); CloseClientSocket(receiveSendEventArgs); //Jump out of the ProcessReceive method. return; } // If no data was received, close the connection. This is a NORMAL // situation that shows when the client has finished sending data. if (receiveSendEventArgs.BytesTransferred == 0) { token.Reset(); CloseClientSocket(receiveSendEventArgs); return; } //Assume that all of the bytes transfered is the received message token.dataMessageReceived = new Byte[receiveSendEventArgs.BytesTransferred]; Buffer.BlockCopy(receiveSendEventArgs.Buffer, token.receiveBufferOffset, token.dataMessageReceived, 0, receiveSendEventArgs.BytesTransferred); // Decode the byte array received in the token //string sBuffer = Encoding.ASCII.GetString(bReceive); string sBuffer = Encoding.UTF8.GetString(token.dataMessageReceived); //At present we will only deal with GET type if (sBuffer.Substring(0, 3) != "GET") { token.Reset(); CloseClientSocket(receiveSendEventArgs); } else { // Look for HTTP request int iStartPos = sBuffer.IndexOf("HTTP", 1); // Get the HTTP text and version e.g. it will return "HTTP/1.1" sHttpVersion = sBuffer.Substring(iStartPos, 8); // Extract the Requested Type and Requested file/directory String sRequest = sBuffer.Substring(5, iStartPos - 1 - 5); sRequest = HttpUtility.UrlDecode(sRequest); token.httpRequest = sRequest.TrimEnd(); StartSendRequest(receiveSendEventArgs); } }
//Post a send. private void StartSend(SocketAsyncEventArgs receiveSendEventArgs) { DataHolderToken token = (DataHolderToken)receiveSendEventArgs.UserToken; int count = 0; try { //The number of bytes to send depends on whether the message is larger than //the buffer or not. If it is larger than the buffer, then we will have //to post more than one send operation. If it is less than or equal to the //size of the send buffer, then we can accomplish it in one send op. if (token.sendBytesRemainingCount <= (receiveBufferSize + settings.send_buffer_size)) { count = token.sendBytesRemainingCount; receiveSendEventArgs.SetBuffer(token.sendBufferOffset, count); //Copy the bytes to the buffer associated with this SAEA object. Buffer.BlockCopy(token.dataToSend, token.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, token.sendBufferOffset, token.sendBytesRemainingCount); } else { logger.Write("Exceeded buffer size in StartSend method, total size = " + token.sendBytesRemainingCount); //We cannot try to set the buffer any larger than its size. //So since receiveSendToken.sendBytesRemainingCount > BufferSize, we just //set it to the maximum size, to send the most data possible. count = receiveBufferSize + settings.send_buffer_size; receiveSendEventArgs.SetBuffer(token.sendBufferOffset, count); //Copy the bytes to the buffer associated with this SAEA object. Buffer.BlockCopy(token.dataToSend, token.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, token.sendBufferOffset, receiveBufferSize); } //post asynchronous send operation bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.SendAsync(receiveSendEventArgs); if (!willRaiseEvent) { ProcessSend(receiveSendEventArgs); } } catch (Exception e) { logger.Write("Exception in SetBuffer in StartSend method of HttpServer.cs, offset=" + token.sendBufferOffset + ", count=" + count + ", Buffer=" + receiveSendEventArgs.Buffer + ": " + e.Message); token.Reset(); CloseClientSocket(receiveSendEventArgs); } }
// Does the normal destroying of sockets after // we finish receiving and sending on a connection. private void CloseClientSocket(SocketAsyncEventArgs e) { DataHolderToken token = (DataHolderToken)e.UserToken; // do a shutdown before you close the socket try { e.AcceptSocket.Shutdown(SocketShutdown.Both); } // throws if socket was already closed catch (Exception) { } //This method closes the socket and releases all resources, both //managed and unmanaged. It internally calls Dispose. e.AcceptSocket.Close(); //Make sure the new DataHolder has been created for the next connection. //If it has, then dataMessageReceived should be null. if (token.dataMessageReceived != null) { token.Reset(); } // Put the SocketAsyncEventArg back into the pool, // to be used by another client. This poolOfRecSendEventArgs.Push(e); // decrement the counter keeping track of the total number of clients //connected to the server, for testing Interlocked.Decrement(ref numConnectedSockets); if (numConnectedSockets + 1 == maxConnections) { logger.Write("Number of connected sockets: " + numConnectedSockets + " / " + maxConnections); } //Release Semaphore so that its connection counter will be decremented. //This must be done AFTER putting the SocketAsyncEventArg back into the pool, //or you can run into problems. maxNumberAcceptedClients.Release(); }
private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs) { DataHolderToken token = (DataHolderToken)receiveSendEventArgs.UserToken; try { //Set the buffer for the receive operation. receiveSendEventArgs.SetBuffer(token.receiveBufferOffset, receiveBufferSize); // Post async receive operation on the socket. bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs); if (!willRaiseEvent) { ProcessReceive(receiveSendEventArgs); } } catch (Exception e) { logger.Write("Exception in SetBuffer in StartReceive method of HttpServer.cs, offset=" + token.receiveBufferOffset + ", count=" + receiveBufferSize + ", Buffer=" + receiveSendEventArgs.Buffer + ": " + e.Message); token.Reset(); CloseClientSocket(receiveSendEventArgs); } }
/// <summary> /// Initializes the server by preallocating reusable buffers and context objects. These objects do not /// need to be preallocated or reused, by is done this way to illustrate how the API can easily be used /// to create reusable objects to increase server performance. /// </summary> public void InitServer() { // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds // against memory fragmentation bufferManager.InitBuffer(); //Allocate the pool of accept operation EventArgs objects //which do not need a buffer for (int i = 0; i < maxSimultaneousAcceptOps; i++) { SocketAsyncEventArgs acceptEventArgs = new SocketAsyncEventArgs(); acceptEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed); poolOfAcceptEventArgs.Push(acceptEventArgs); } //Allocate the pool of receive/send operation EventArgs objects //which DO need a buffer for (int i = 0; i < numberOfEventArgsForRecSend; i++) { SocketAsyncEventArgs sendReceiveEventArgs = new SocketAsyncEventArgs(); bufferManager.SetBuffer(sendReceiveEventArgs); sendReceiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); DataHolderToken token = new DataHolderToken(sendReceiveEventArgs.Offset, receiveBufferSize); sendReceiveEventArgs.UserToken = token; poolOfRecSendEventArgs.Push(sendReceiveEventArgs); } }
void NewRequestThread(Object o) { SocketAsyncEventArgs e = (SocketAsyncEventArgs)o; DataHolderToken token = (DataHolderToken)e.UserToken; OpResult opResult = new OpResult(OpStatusCode.BadRequest); string sCommand = ""; string sParam = ""; string sBody = ""; try { // Show error for index if (token.httpRequest.Length == 0) { sCommand = "<i>No command specified.</i>"; sParam = "<i>No parameters specified.</i>"; } else { string[] req = token.httpRequest.Split(new char[] { '?' }, 2); //Strip off "?" string[] cmd_stack = req[0].Split(new char[] { '/' }); for (int idx = 0; idx < cmd_stack.Length; idx++) { string[] command = cmd_stack[idx].Split(new char[] { ' ' }, 2); if (command.Length == 0) { return; } sCommand = command[0]; sParam = (command.Length == 2 ? command[1] : string.Empty); if (sCommand.Equals("help", StringComparison.InvariantCultureIgnoreCase)) { opResult = remoteCommands.CommandListHTML(AddInModule.GetPortNumber(AddInModule.m_basePortNumber)); } else if (sCommand.Equals("format", StringComparison.InvariantCultureIgnoreCase)) { ICommand formatter = new customCmd(sBody); opResult = formatter.Execute(sParam); sBody = ""; } else if (token.opResult != null) { opResult = token.opResult; } else if (sCommand.Equals("server-settings")) { opResult = ExecuteSettingsCommand(sCommand, sParam); } else if (sCommand.Equals("music-clear-cache")) { startCacheBuildNow(); opResult = new OpResult(); opResult.StatusCode = OpStatusCode.Success; opResult.StatusText = "Cache cleared"; } else { opResult = remoteCommands.Execute(sCommand, sParam, settings); } } } //Get bytes to send to browser if (opResult.isHelpFormat()) { string sTempBody = opResult.ToString(); if (sParam.Length == 0) { sParam = "<i>No parameters specified.</i>"; } if (opResult.StatusCode != OpStatusCode.Ok && opResult.StatusCode != OpStatusCode.Success) { sTempBody = string.Format("<h1>ERROR<hr>Command: {0}<br>Params: {1}<br>Returned: {2} - {3}<hr>See <a href='help'>Help</a></h1>", sCommand, sParam, opResult.StatusCode, opResult.ToString()); } else if (opResult.StatusCode != OpStatusCode.OkImage) { if (sTempBody.Length > 0) { if (sTempBody.TrimStart()[0] != '<') { sTempBody = "<pre>" + sTempBody + "</pre>"; } } else { sTempBody = string.Format("<h1>Ok<hr>Last Command: '{0}'<br>Params: {1}<br>Returned: {2}<hr>See <a href='help'>Help</a></h1>", sCommand, sParam, opResult.StatusCode); } } if (sBody.Length > 0) { sBody += "<HR>"; } sBody += sTempBody; token.dataToSend = GetPageDataToSend(string.Format("{0}\r\n", sBody)); } else if (opResult.StatusCode != OpStatusCode.OkImage) { token.dataToSend = GetPageJsonDataToSend(opResult.ToString()); } else { token.dataToSend = GetImageDataToSend(opResult); } } catch (Exception ex) { logger.Write("Exception in NewRequestThread: " + ex.Message); token.dataToSend = GetPageDataToSend(string.Format("<html><body>EXCEPTION: {0}<hr></body></html>", ex.Message)); Trace.TraceError(ex.ToString()); } //Set send operation variables token.sendBytesRemainingCount = token.dataToSend.Length; token.bytesSentAlreadyCount = 0; StartSend(e); }
void StartSendRequestThread(Object o) { System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture; SocketAsyncEventArgs e = (SocketAsyncEventArgs)o; DataHolderToken token = (DataHolderToken)e.UserToken; string[] command = getCommandsFromArgs(e); string sCommand = command[0]; string sParam = (command.Length == 2 ? command[1] : string.Empty); Thread http_thread = new Thread(new ParameterizedThreadStart(NewRequestThread)); http_thread.IsBackground = true; http_thread.SetApartmentState(ApartmentState.MTA); if (sCommand.Equals("music-list-playing") || sCommand.Equals("music-list-current") || sCommand.StartsWith("play") || sCommand.Equals("music-shuffle")) { RemotedWindowsMediaPlayer remotedPlayer = null; try { //Only allow one thread at a time to access remoted player waitHandle.WaitOne(); remotedPlayer = new RemotedWindowsMediaPlayer(); remotedPlayer.CreateControl(); token.opResult = new OpResult(); if (sCommand.Equals("music-list-playing")) { if (sParam != null && sParam.Length != 0) { string sIndex = sParam.Substring(sParam.IndexOf("index:") + "index:".Length); if (remotedPlayer.setNowPlaying(Int16.Parse(sIndex))) { token.opResult.StatusCode = OpStatusCode.Success; token.opResult.StatusText = "Current media set to index " + sIndex; } else { token.opResult.StatusCode = OpStatusCode.BadRequest; token.opResult.StatusText = "Current playback item not set"; } } else { token.opResult.StatusCode = OpStatusCode.Success; NowPlaying nowPlaying = new NowPlaying(remotedPlayer); token.opResult.ContentObject = nowPlaying; } } else if (sCommand.StartsWith("play")) { //For playrate and playstate-get commands token.opResult = remoteCommands.Execute(remotedPlayer, sCommand, sParam); } else if (sCommand.Equals("music-shuffle")) { remotedPlayer.setShuffleMode(); token.opResult.StatusCode = OpStatusCode.Success; token.opResult.StatusText = "Shuffle mode set to true"; } else { //"music-list-current" command token.opResult.StatusCode = OpStatusCode.Success; CurrentState state = new CurrentState(remotedPlayer); token.opResult.ContentObject = state; } } catch (Exception c) { logger.Write("Exception in StartSendRequestThread: " + c.Message); token.opResult.StatusCode = OpStatusCode.Exception; token.opResult.StatusText = c.Message; } finally { if (remotedPlayer != null) { remotedPlayer.Dispose(); } waitHandle.Set(); } } http_thread.Start(e); }