//____________________________________________________________________________ // initializes the server by preallocating reusable buffers and // context objects (SocketAsyncEventArgs objects). //It is NOT mandatory that you preallocate them or reuse them. But, but it is //done this way to illustrate how the API can // easily be used to create reusable objects to increase server performance. internal void Init() { // Allocate one large byte buffer block, which all I/O operations will //use a piece of. This gaurds against memory fragmentation. this.theBufferManager.InitBuffer(); // preallocate pool of SocketAsyncEventArgs objects for accept operations for (Int32 i = 0; i < this.socketListenerSettings.MaxAcceptOps; i++) { // add SocketAsyncEventArg to the pool this.poolOfAcceptEventArgs.Push(CreateNewSaeaForAccept(poolOfAcceptEventArgs)); } //The pool that we built ABOVE is for SocketAsyncEventArgs objects that do // accept operations. //Now we will build a separate pool for SAEAs objects //that do receive/send operations. One reason to separate them is that accept //operations do NOT need a buffer, but receive/send operations do. //ReceiveAsync and SendAsync require //a parameter for buffer size in SocketAsyncEventArgs.Buffer. // So, create pool of SAEA objects for receive/send operations. SocketAsyncEventArgs eventArgObjectForPool; Int32 tokenId; for (Int32 i = 0; i < this.socketListenerSettings.NumberOfSaeaForRecSend; i++) { //Allocate the SocketAsyncEventArgs object for this loop, //to go in its place in the stack which will be the pool //for receive/send operation context objects. eventArgObjectForPool = new SocketAsyncEventArgs(); // assign a byte buffer from the buffer block to //this particular SocketAsyncEventArg object this.theBufferManager.SetBuffer(eventArgObjectForPool); tokenId = poolOfRecSendEventArgs.AssignTokenId() + 1000000; //Attach the SocketAsyncEventArgs object //to its event handler. Since this SocketAsyncEventArgs object is //used for both receive and send operations, whenever either of those //completes, the IO_Completed method will be called. eventArgObjectForPool.Completed += new EventHandler <SocketAsyncEventArgs>(IO_Completed); //We can store data in the UserToken property of SAEA object. DataHoldingUserToken theTempReceiveSendUserToken = new DataHoldingUserToken(eventArgObjectForPool, eventArgObjectForPool.Offset, eventArgObjectForPool.Offset + this.socketListenerSettings.BufferSize, this.socketListenerSettings.ReceivePrefixLength, this.socketListenerSettings.SendPrefixLength, tokenId); //We'll have an object that we call DataHolder, that we can remove from //the UserToken when we are finished with it. So, we can hang on to the //DataHolder, pass it to an app, serialize it, or whatever. theTempReceiveSendUserToken.CreateNewDataHolder(); eventArgObjectForPool.UserToken = theTempReceiveSendUserToken; // add this SocketAsyncEventArg object to the pool. this.poolOfRecSendEventArgs.Push(eventArgObjectForPool); } }
//____________________________________________________________________________ // initializes the server by preallocating reusable buffers and // context objects (SocketAsyncEventArgs objects). //It is NOT mandatory that you preallocate them or reuse them. But, but it is //done this way to illustrate how the API can // easily be used to create reusable objects to increase server performance. internal void Init() { if (Program.watchProgramFlow == true) //for testing { Program.testWriter.WriteLine("Init method"); } if (Program.watchThreads == true) //for testing { DealWithThreadsForTesting("Init()"); } // Allocate one large byte buffer block, which all I/O operations will //use a piece of. This gaurds against memory fragmentation. this.theBufferManager.InitBuffer(); if (Program.watchProgramFlow == true) //for testing { Program.testWriter.WriteLine("Starting creation of accept SocketAsyncEventArgs pool:"); } // preallocate pool of SocketAsyncEventArgs objects for accept operations for (Int32 i = 0; i < this.socketListenerSettings.MaxAcceptOps; i++) { // add SocketAsyncEventArg to the pool this.poolOfAcceptEventArgs.Push(CreateNewSaeaForAccept(poolOfAcceptEventArgs)); } //The pool that we built ABOVE is for SocketAsyncEventArgs objects that do // accept operations. //Now we will build a separate pool for SAEAs objects //that do receive/send operations. One reason to separate them is that accept //operations do NOT need a buffer, but receive/send operations do. //ReceiveAsync and SendAsync require //a parameter for buffer size in SocketAsyncEventArgs.Buffer. // So, create pool of SAEA objects for receive/send operations. SocketAsyncEventArgs eventArgObjectForPool; if (Program.watchProgramFlow == true) //for testing { Program.testWriter.WriteLine("Starting creation of receive/send SocketAsyncEventArgs pool"); } Int32 tokenId; for (Int32 i = 0; i < this.socketListenerSettings.NumberOfSaeaForRecSend; i++) { //Allocate the SocketAsyncEventArgs object for this loop, //to go in its place in the stack which will be the pool //for receive/send operation context objects. eventArgObjectForPool = new SocketAsyncEventArgs(); // assign a byte buffer from the buffer block to //this particular SocketAsyncEventArg object this.theBufferManager.SetBuffer(eventArgObjectForPool); tokenId = poolOfRecSendEventArgs.AssignTokenId() + 1000000; //Attach the SocketAsyncEventArgs object //to its event handler. Since this SocketAsyncEventArgs object is //used for both receive and send operations, whenever either of those //completes, the IO_Completed method will be called. eventArgObjectForPool.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); //We can store data in the UserToken property of SAEA object. DataHoldingUserToken theTempReceiveSendUserToken = new DataHoldingUserToken(eventArgObjectForPool, eventArgObjectForPool.Offset, eventArgObjectForPool.Offset + this.socketListenerSettings.BufferSize, this.socketListenerSettings.ReceivePrefixLength, this.socketListenerSettings.SendPrefixLength, tokenId); //We'll have an object that we call DataHolder, that we can remove from //the UserToken when we are finished with it. So, we can hang on to the //DataHolder, pass it to an app, serialize it, or whatever. theTempReceiveSendUserToken.CreateNewDataHolder(); eventArgObjectForPool.UserToken = theTempReceiveSendUserToken; // add this SocketAsyncEventArg object to the pool. this.poolOfRecSendEventArgs.Push(eventArgObjectForPool); } }
//____________________________________________________________________________ // This method is invoked by the IO_Completed method // when an asynchronous receive operation completes. // If the remote host closed the connection, then the socket is closed. // Otherwise, we process the received data. And if a complete message was // received, then we do some additional processing, to // respond to the client. private void ProcessReceive(SocketAsyncEventArgs receiveSendEventArgs) { DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken; // If there was a socket error, close the connection. This is NOT a normal // situation, if you get an error here. // In the Microsoft example code they had this error situation handled // at the end of ProcessReceive. Putting it here improves readability // by reducing nesting some. if (receiveSendEventArgs.SocketError != SocketError.Success) { receiveSendToken.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) { receiveSendToken.Reset(); CloseClientSocket(receiveSendEventArgs); return; } //The BytesTransferred property tells us how many bytes //we need to process. Int32 remainingBytesToProcess = receiveSendEventArgs.BytesTransferred; //If we have not got all of the prefix already, //then we need to work on it here. if (receiveSendToken.receivedPrefixBytesDoneCount < this.socketListenerSettings.ReceivePrefixLength) { remainingBytesToProcess = prefixHandler.HandlePrefix(receiveSendEventArgs, receiveSendToken, remainingBytesToProcess); if (remainingBytesToProcess == 0) { // We need to do another receive op, since we do not have // the message yet, but remainingBytesToProcess == 0. StartReceive(receiveSendEventArgs); //Jump out of the method. return; } } // If we have processed the prefix, we can work on the message now. // We'll arrive here when we have received enough bytes to read // the first byte after the prefix. bool incomingTcpMessageIsReady = messageHandler.HandleMessage(receiveSendEventArgs, receiveSendToken, remainingBytesToProcess); if (incomingTcpMessageIsReady == true) { if (Program.watchData == true) { Program.testWriter.WriteLine(receiveSendToken.TokenId + ", Message in DataHolder = " + Encoding.ASCII.GetString(receiveSendToken.theDataHolder.dataMessageReceived) + "\r\n"); } //for testing only if (Program.msDelayAfterGettingMessage > -1) { //A Thread.Sleep here can be used to simulate delaying the //return of the SocketAsyncEventArgs object for receive/send //to the pool. Simulates doing some work here. if (Program.watchData == true) { Program.testWriter.WriteLine(receiveSendToken.TokenId + " waiting after read.\r\n"); } Thread.Sleep(Program.msDelayAfterGettingMessage); } // Pass the DataHolder object to the Mediator here. The data in // this DataHolder can be used for all kinds of things that an // intelligent and creative person like you might think of. receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder); // Create a new DataHolder for next message. receiveSendToken.CreateNewDataHolder(); //Reset the variables in the UserToken, to be ready for the //next message that will be received on the socket in this //SAEA object. receiveSendToken.Reset(); receiveSendToken.theMediator.PrepareOutgoingData(); StartSend(receiveSendToken.theMediator.GiveBack()); } else { // Since we have NOT gotten enough bytes for the whole message, // we need to do another receive op. Reset some variables first. // All of the data that we receive in the next receive op will be // message. None of it will be prefix. So, we need to move the // receiveSendToken.receiveMessageOffset to the beginning of the // receive buffer space for this SAEA. receiveSendToken.receiveMessageOffset = receiveSendToken.bufferOffsetReceive; // Do NOT reset receiveSendToken.receivedPrefixBytesDoneCount here. // Just reset recPrefixBytesDoneThisOp. receiveSendToken.recPrefixBytesDoneThisOp = 0; StartReceive(receiveSendEventArgs); } }