public Int32 HandlePrefix(SocketAsyncEventArgs e, DataHoldingUserToken receiveSendToken, Int32 remainingBytesToProcess) { //receivedPrefixBytesDoneCount tells us how many prefix bytes were //processed during previous receive ops which contained data for //this message. Usually there will NOT have been any previous //receive ops here. So in that case, //receiveSendToken.receivedPrefixBytesDoneCount would equal 0. //Create a byte array to put the new prefix in, if we have not //already done it in a previous loop. if (receiveSendToken.receivedPrefixBytesDoneCount == 0) { receiveSendToken.byteArrayForPrefix = new Byte[receiveSendToken.receivePrefixLength]; } // If this next if-statement is true, then we have received >= // enough bytes to have the prefix. So we can determine the // length of the message that we are working on. if (remainingBytesToProcess >= receiveSendToken.receivePrefixLength - receiveSendToken.receivedPrefixBytesDoneCount) { //Now copy that many bytes to byteArrayForPrefix. //We can use the variable receiveMessageOffset as our main //index to show which index to get data from in the TCP //buffer. Buffer.BlockCopy(e.Buffer, receiveSendToken.receiveMessageOffset - receiveSendToken.receivePrefixLength + receiveSendToken.receivedPrefixBytesDoneCount, receiveSendToken.byteArrayForPrefix, receiveSendToken.receivedPrefixBytesDoneCount, receiveSendToken.receivePrefixLength - receiveSendToken.receivedPrefixBytesDoneCount); remainingBytesToProcess = remainingBytesToProcess - receiveSendToken.receivePrefixLength + receiveSendToken.receivedPrefixBytesDoneCount; receiveSendToken.recPrefixBytesDoneThisOp = receiveSendToken.receivePrefixLength - receiveSendToken.receivedPrefixBytesDoneCount; receiveSendToken.receivedPrefixBytesDoneCount = receiveSendToken.receivePrefixLength; receiveSendToken.lengthOfCurrentIncomingMessage = BitConverter.ToInt32(receiveSendToken.byteArrayForPrefix, 0); if (receiveSendToken.lengthOfCurrentIncomingMessage == -100) return receiveSendToken.lengthOfCurrentIncomingMessage; } //This next else-statement deals with the situation //where we have some bytes //of this prefix in this receive operation, but not all. else { //Write the bytes to the array where we are putting the //prefix data, to save for the next loop. Buffer.BlockCopy(e.Buffer, receiveSendToken.receiveMessageOffset - receiveSendToken.receivePrefixLength + receiveSendToken.receivedPrefixBytesDoneCount, receiveSendToken.byteArrayForPrefix, receiveSendToken.receivedPrefixBytesDoneCount, remainingBytesToProcess); receiveSendToken.recPrefixBytesDoneThisOp = remainingBytesToProcess; receiveSendToken.receivedPrefixBytesDoneCount += remainingBytesToProcess; remainingBytesToProcess = 0; } // This section is needed when we have received // an amount of data exactly equal to the amount needed for the prefix, // but no more. And also needed with the situation where we have received // less than the amount of data needed for prefix. if (remainingBytesToProcess == 0) { receiveSendToken.receiveMessageOffset = receiveSendToken.receiveMessageOffset - receiveSendToken.recPrefixBytesDoneThisOp; receiveSendToken.recPrefixBytesDoneThisOp = 0; } return remainingBytesToProcess; }
public ReceiveArgs(DataHoldingUserToken token) { this.Token = token; }
//____________________________________________________________________________ // 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. public 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(); //this.theBufferManagerSend.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 eventArgRecObjectForPool; //SocketAsyncEventArgs eventArgSendObjectForPool; //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. eventArgRecObjectForPool = new SocketAsyncEventArgs(); //eventArgSendObjectForPool = new SocketAsyncEventArgs(); // assign a byte buffer from the buffer block to //this particular SocketAsyncEventArg object this.theBufferManager.SetBuffer(eventArgRecObjectForPool); //this.theBufferManagerSend.SetBuffer(eventArgSendObjectForPool); int tid = poolOfRecEventArgs.AssignTokenId(); tokenId = tid + 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. eventArgRecObjectForPool.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); //eventArgSendObjectForPool.Completed += new EventHandler<SocketAsyncEventArgs>(IO_CompletedSend); //We can store data in the UserToken property of SAEA object. // DataHoldingUserToken theTempReceiveSendUserToken = new DataHoldingUserToken(eventArgRecObjectForPool, eventArgRecObjectForPool.Offset, eventArgRecObjectForPool.Offset + this.socketListenerSettings.BufferSize, this.socketListenerSettings.ReceivePrefixLength, this.socketListenerSettings.SendPrefixLength, tokenId); DataHoldingUserToken theTempReceiveSendUserToken = new DataHoldingUserToken(eventArgRecObjectForPool, eventArgRecObjectForPool.Offset, eventArgRecObjectForPool.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(); eventArgRecObjectForPool.UserToken = theTempReceiveSendUserToken; //eventArgSendObjectForPool.UserToken = theTempReceiveSendUserToken; // add this SocketAsyncEventArg object to the pool. this.poolOfRecEventArgs.Push(eventArgRecObjectForPool); //this.poolOfSendEventArgs.Push(eventArgSendObjectForPool); } }
public bool HandleMessage(SocketAsyncEventArgs receiveSendEventArgs, DataHoldingUserToken receiveSendToken, Int32 remainingBytesToProcess) { bool incomingTcpMessageIsReady = false; //Create the array where we'll store the complete message, //if it has not been created on a previous receive op. Log.Info("HandleMessage:接收到的数据长度为:remainingBytesToProcess:" + remainingBytesToProcess + "receiveSendToken.receivedMessageBytesDoneCount:" + receiveSendToken.receivedMessageBytesDoneCount+"receiveSendToken.lengthOfCurrentIncomingMessage:" + receiveSendToken.lengthOfCurrentIncomingMessage); if (receiveSendToken.receivedMessageBytesDoneCount == 0) { receiveSendToken.theDataHolder.dataMessageReceived = new Byte[receiveSendToken.lengthOfCurrentIncomingMessage]; } // Remember there is a receiveSendToken.receivedPrefixBytesDoneCount // variable, which allowed us to handle the prefix even when it // requires multiple receive ops. In the same way, we have a // receiveSendToken.receivedMessageBytesDoneCount variable, which // helps us handle message data, whether it requires one receive // operation or many. if (remainingBytesToProcess + receiveSendToken.receivedMessageBytesDoneCount == receiveSendToken.lengthOfCurrentIncomingMessage) { // If we are inside this if-statement, then we got // the end of the message. In other words, // the total number of bytes we received for this message matched the // message length value that we got from the prefix. //if (Program.watchProgramFlow == true) //for testing //{ // Program.testWriter.WriteLine("MessageHandler, length is right for " + receiveSendToken.TokenId); //} // Write/append the bytes received to the byte array in the // DataHolder object that we are using to store our data. Buffer.BlockCopy(receiveSendEventArgs.Buffer, receiveSendToken.receiveMessageOffset, receiveSendToken.theDataHolder.dataMessageReceived, receiveSendToken.receivedMessageBytesDoneCount, remainingBytesToProcess); Log.Info("HandleMessage:incomingTcpMessageIsReady 完成"); incomingTcpMessageIsReady = true; } else { // If we are inside this else-statement, then that means that we // need another receive op. We still haven't got the whole message, // even though we have examined all the data that was received. // Not a problem. In SocketListener.ProcessReceive we will just call // StartReceive to do another receive op to receive more data. //if (Program.watchProgramFlow == true) //for testing //{ // Program.testWriter.WriteLine(" MessageHandler, length is short for " + receiveSendToken.TokenId); //} try { Buffer.BlockCopy(receiveSendEventArgs.Buffer, receiveSendToken.receiveMessageOffset, receiveSendToken.theDataHolder.dataMessageReceived, receiveSendToken.receivedMessageBytesDoneCount, remainingBytesToProcess); } catch (Exception e) { Log.Error("receiveSendEventArgs.Buffer.Length:" + receiveSendEventArgs.Buffer.Length + " receiveSendToken.receiveMessageOffset:" + receiveSendToken.receiveMessageOffset + " receiveSendToken.theDataHolder.dataMessageReceived.Length:" + receiveSendToken.theDataHolder.dataMessageReceived.Length + " receiveSendToken.receivedMessageBytesDoneCount:" + receiveSendToken.receivedMessageBytesDoneCount + " remainingBytesToProcess:" + remainingBytesToProcess + " " + e.Message + " 接收到的数据长度为:" + receiveSendToken.lengthOfCurrentIncomingMessage + " receiveSendToken.receivedPrefixBytesDoneCount:" + receiveSendToken.receivedPrefixBytesDoneCount); throw; } receiveSendToken.receiveMessageOffset = receiveSendToken.receiveMessageOffset - receiveSendToken.recPrefixBytesDoneThisOp; receiveSendToken.receivedMessageBytesDoneCount += remainingBytesToProcess; Log.Info("HandleMessage:持续接收中: receiveSendToken.receivedMessageBytesDoneCount=" + receiveSendToken.receivedMessageBytesDoneCount); } return incomingTcpMessageIsReady; }
//____________________________________________________________________________ //Display thread info. //Note that there is NOT a 1:1 ratio between managed threads //and system (native) threads. // //Overloaded. //Use this one after the DataHoldingUserToken is available. // private void DealWithThreadsForTesting(string methodName, DataHoldingUserToken receiveSendToken) { StringBuilder sb = new StringBuilder(); sb.Append(" In " + methodName + ", receiveSendToken id " + receiveSendToken.TokenId + ". Thread id " + Thread.CurrentThread.ManagedThreadId + ". Socket handle " + receiveSendToken.socketHandleNumber + "."); sb.Append(DealWithNewThreads()); Program.testWriter.WriteLine(sb.ToString()); }