예제 #1
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="e"></param>
        private void ProcessSend(SocketAsyncEventArgs receiveSendEventArgs)
        {
            DataHoldingUserToken receiveSendToken =
                (DataHoldingUserToken)receiveSendEventArgs.UserToken;

            receiveSendToken.sendBytesRemainingCount =
                receiveSendToken.sendBytesRemainingCount
                - receiveSendEventArgs.BytesTransferred;
            receiveSendToken.bytesSentAlreadyCount +=
                receiveSendEventArgs.BytesTransferred;

            if (receiveSendEventArgs.SocketError == SocketError.Success)
            {
                if (receiveSendToken.sendBytesRemainingCount == 0)
                {
                    StartReceive(receiveSendEventArgs);
                }
                else
                {
                    //If some of the bytes in the message have NOT been sent,
                    //then we will need to post another send operation.
                    //So let's loop back to StartSend().
                    StartSend(receiveSendEventArgs);
                }
            }
            else
            {
                //If we are in this else-statement, there was a socket error.
                //In this example we'll just close the socket if there was a socket error
                //when receiving data from the client.
                receiveSendToken.Reset();
                CloseClientSocket(receiveSendEventArgs);
            }
        }
예제 #2
0
        /// <summary>
        /// Post a send op.
        /// </summary>
        /// <param name="socketAsyncEventArgs"></param>
        private void StartSend(SocketAsyncEventArgs receiveSendEventArgs)
        {
            DataHoldingUserToken receiveSendToken =
                (DataHoldingUserToken)receiveSendEventArgs.UserToken;

            //Set the buffer. You can see on Microsoft's page at
            //http://msdn.microsoft.com/en-us/library/
            //         system.net.sockets.socketasynceventargs.setbuffer.aspx
            //that there are two overloads. One of the overloads has 3 parameters.
            //When setting the buffer, you need 3 parameters the first time you set it,
            //which we did in the Init method. The first of the three parameters
            //tells what byte array to use as the buffer. After we tell what byte array
            //to use we do not need to use the overload with 3 parameters any more.
            //(That is the whole reason for using the buffer block. You keep the same
            //byte array as buffer always, and keep it all in one block.)
            //Now we use the overload with two parameters. We tell
            // (1) the offset and
            // (2) the number of bytes to use, starting at the offset.

            //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 (receiveSendToken.sendBytesRemainingCount
                <= this.socketListenerSettings.BufferSize)
            {
                receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend,
                                               receiveSendToken.sendBytesRemainingCount);
                //Copy the bytes to the buffer associated with this SAEA object.
                Buffer.BlockCopy(receiveSendToken.dataToSend,
                                 receiveSendToken.bytesSentAlreadyCount,
                                 receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend,
                                 receiveSendToken.sendBytesRemainingCount);
            }
            else
            {
                //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.
                receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend,
                                               this.socketListenerSettings.BufferSize);
                //Copy the bytes to the buffer associated with this SAEA object.
                Buffer.BlockCopy(receiveSendToken.dataToSend,
                                 receiveSendToken.bytesSentAlreadyCount,
                                 receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend,
                                 this.socketListenerSettings.BufferSize);

                //We'll change the value of sendUserToken.sendBytesRemainingCount
                //in the ProcessSend method.
            }

            //post asynchronous send operation
            bool willRaiseEvent =
                receiveSendEventArgs.AcceptSocket.SendAsync(receiveSendEventArgs);

            if (!willRaiseEvent)
            {
                ProcessSend(receiveSendEventArgs);
            }
        }
예제 #3
0
        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.
            if (receiveSendToken.receivedMessageBytesDoneCount == 0)
            {
                receiveSendToken.theDataHolder.dataMessageReceived = new Byte[receiveSendToken.lengthOfCurrentIncomingMessage];
            }

            // Rememer there is a receiveSendToken.receivedPrefixBytesDoneCount
            // variable, which allowed us to handle the prefix even when it
            // requires mutiple receive ops. In the same way, we have a
            // receiveSendToken.receivedMessageBytesDoneCount varialbe, 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.

                // 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);

                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.ProcessReceived we will just call
                // StartReceive to do another receive op to receive more data.
                Buffer.BlockCopy(receiveSendEventArgs.Buffer,
                                 receiveSendToken.receiveMessageOffset,
                                 receiveSendToken.theDataHolder.dataMessageReceived,
                                 receiveSendToken.receivedMessageBytesDoneCount,
                                 remainingBytesToProcess);

                receiveSendToken.receiveMessageOffset =
                    receiveSendToken.receiveMessageOffset - receiveSendToken.recPrefixBytesDoneThisOp;

                receiveSendToken.receivedMessageBytesDoneCount += remainingBytesToProcess;
            }
            return(incomingTcpMessageIsReady);
        }
예제 #4
0
        internal DataHolder HandleReceivedData(DataHolder incomingDataHolder, SocketAsyncEventArgs theSaeaObject)
        {
            DataHoldingUserToken receiveToken = (DataHoldingUserToken)theSaeaObject.UserToken;

            theDataHolder           = incomingDataHolder;
            theDataHolder.sessionId = receiveToken.SessionId;
            theDataHolder.receivedTransMissionId = this.ReceivedTransMissionIdGetter();
            theDataHolder.remoteEndPoint         = this.GetRemoteEndpoint();
            this.AddDataHolder();
            return(theDataHolder);
        }
예제 #5
0
        internal void PrepareOutgoingData(SocketAsyncEventArgs e,
                                          DataHolder handledDataHolder)
        {
            DataHoldingUserToken theUserToken = (DataHoldingUserToken)e.UserToken;

            theDataHolder = handledDataHolder;

            // In this example code, we will send back the receivedTransMissionId,
            // followed by the message that the client sent to the server. And we
            // must prefix it with the length of the message. So we put 3 things in to the array.
            // 1) prefix,
            // 2) receivedTransMissionId,
            // 3) the message that we received from the client, which we stored in
            //    our DataHolder until we needed it.
            // That is our communication protocal. The client must know the protocal.

            // Convert the receivedTransMissionId to byte array.
            Byte[] idByteArray = BitConverter.GetBytes(theDataHolder.receivedTransMissionId);

            // Determine the length of all the data that we will send back.
            Int32 lengthOfCurrentOutgoingMessage = idByteArray.Length
                                                   + theDataHolder.dataMessageReceived.Length;

            // So, now we convert the length integer into a byte array.
            Byte[] arrayOfBytesInPrefix = BitConverter.GetBytes(lengthOfCurrentOutgoingMessage);

            // Create the byte array to send.
            theUserToken.dataToSend = new Byte[theUserToken.sendPrefixLength + lengthOfCurrentOutgoingMessage];

            // Now copy the 3 things to the theUserToken.dataToSend.
            Buffer.BlockCopy(arrayOfBytesInPrefix, 0, theUserToken.dataToSend, 0, theUserToken.sendPrefixLength);
            Buffer.BlockCopy(idByteArray, 0, theUserToken.dataToSend, theUserToken.sendPrefixLength, idByteArray.Length);
            // The message that the client sent is already in a byte array, in DataHolder.
            Buffer.BlockCopy(theDataHolder.dataMessageReceived, 0, theUserToken.dataToSend,
                             theUserToken.sendPrefixLength + idByteArray.Length, theDataHolder.dataMessageReceived.Length);

            theUserToken.sendBytesRemainingCount = theUserToken.sendPrefixLength + lengthOfCurrentOutgoingMessage;
            theUserToken.bytesSentAlreadyCount   = 0;
        }
예제 #6
0
        /// <summary>
        /// Set the receive buffer and post a receive op.
        /// </summary>
        /// <param name="receiveSendEventArgs"></param>
        private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs)
        {
            DataHoldingUserToken receiveSendToken =
                (DataHoldingUserToken)receiveSendEventArgs.UserToken;

            // Set the buffer for the receive operation.
            receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetReceive,
                                           this.socketListenerSettings.BufferSize);

            // Post async receive operation on the socket.
            bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs);

            // Socket.ReceiveAsync returns true if the I/O operation is pending. The
            // SocketAsyncEventArgs.Completed event on the e parameter will be raised
            // upon completion of the operation. So, true will cause the IO_Completed
            // method to be called when the receive operation completes.
            // That's because of the event handler we created when building
            // the pool of SocketAsyncEventArgs objects that perform receive/send.
            // It was the line that said
            // eventArgObjectForPool.Completed +=
            //     new EventHandler<SocketAsyncEventArgs>(IO_Completed);

            // Socket.ReceiveAsync returns false if I/O operation completed synchronously.
            // In that case, the SocketAsyncEventArgs.Completed event on the e parameter
            // will not be raised and the e object passed as a parameter may be
            // examined immediately after the method call
            // returns to retrieve the result of the operation.
            // It may be false in the case of a socket error.
            if (!willRaiseEvent)
            {
                // If the op completed synchronously, we need to call ProcessReceive
                // method directly. This will probably be used rarely, as you will
                // see in testing.
                ProcessReceive(receiveSendEventArgs);
            }
        }
예제 #7
0
        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 at
            // least 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);

                return(remainingBytesToProcess);
            }

            // 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);
        }
예제 #8
0
        /// <summary>
        /// Initailizes the server by preallocating reusable buffers and
        /// context objects (SocketAsyncEventArgs objects).
        /// It is NOT mandatory that you preallocate them or reuse them. But, it is
        /// done this way to illustrate how the API can easily be used
        /// to create reusable objects to increase server performance.
        /// </summary>
        internal void Init()
        {
            // Allocate one large byte buffer block, which all I/O operations will
            // use a piece of. This guards against memory fragmentation.
            this.theBufferManager.InitBuffer();

            // Preallocate pool of SocketAsyncEventArgs objects for accept operations
            for (Int32 i = 0; i < this.socketListenerSettings.MaxAcceptOps; i++)
            {
                // Add SocketAsyncEventArgs 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 SocketAsyncEvnetArgs.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 n 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 pool.
                this.poolOfRecSendEventArgs.Push(eventArgObjectForPool);
            }
        }
예제 #9
0
        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="receiveSendEventArgs"></param>
        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)
            {
                // 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;

                // Since we have not gotten enough bytes for the whole message,
                // we need to do another receive op.
                StartReceive(receiveSendEventArgs);
            }
        }