Пример #1
0
        //____________________________________________________________________________
        // Connect to the host.
        //
        private void StartConnect(SocketAsyncEventArgs connectEventArgs)
        {
            //Cast SocketAsyncEventArgs.UserToken to our state object.
            ConnectOpUserToken theConnectingToken = (ConnectOpUserToken)connectEventArgs.UserToken;

            if (Program.watchProgramFlow == true)   //for testing
            {
                Program.testWriter.WriteLine("StartConnect, connect id = " + theConnectingToken.TokenId);
            }

            //SocketAsyncEventArgs object that do connect operations on the client
            //are different from those that do accept operations on the server.
            //On the server the listen socket had EndPoint info. And that info was
            //passed from the listen socket to the SocketAsyncEventArgs object
            //that did the accept operation.
            //But on the client there is no listen socket. The connect socket
            //needs the info on the Remote Endpoint.
            connectEventArgs.RemoteEndPoint = this.socketClientSettings.ServerEndPoint;

            connectEventArgs.AcceptSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //Post the connect operation on the socket.
            //A local port is assigned by the Windows OS during connect op.
            bool willRaiseEvent = connectEventArgs.AcceptSocket.ConnectAsync(connectEventArgs);

            if (!willRaiseEvent)
            {
                if (Program.watchProgramFlow == true)   //for testing
                {
                    Program.testWriter.WriteLine("StartConnect method if (!willRaiseEvent), id = " + theConnectingToken.TokenId);
                }

                ProcessConnect(connectEventArgs);
            }
        }
Пример #2
0
        //____________________________________________________________________________
        //
        private void PushMessageArray(OutgoingMessageHolder theOutgoingMessageHolder)
        {
            SocketAsyncEventArgs connectEventArgs;

            //Get a SocketAsyncEventArgs object to connect with.

            //Get it from the pool if there is more than one.
            connectEventArgs = this.poolOfConnectEventArgs.Pop();
            //or make a new one.
            if (connectEventArgs == null)
            {
                connectEventArgs = CreateNewSaeaForConnect(poolOfConnectEventArgs);
            }

            ConnectOpUserToken theConnectingToken = (ConnectOpUserToken)connectEventArgs.UserToken;

            if (Program.watchProgramFlow == true)   //for testing
            {
                Program.testWriter.WriteLine("PushMessageArray, connect id = " + theConnectingToken.TokenId);
            }

            theConnectingToken.outgoingMessageHolder = theOutgoingMessageHolder;

            StartConnect(connectEventArgs);
            //Loop back to get message(s) for next connection.
            CheckStack();
        }
Пример #3
0
        //____________________________________________________________________________
        // This method is called when an operation is completed on a socket
        //
        private void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            // determine which type of operation just completed and call the associated handler
            switch (e.LastOperation)
            {
            case SocketAsyncOperation.Connect:
                if (Program.watchProgramFlow == true)       //for testing
                {
                    ConnectOpUserToken theConnectingToken = (ConnectOpUserToken)e.UserToken;
                    Program.testWriter.WriteLine("\r\nIO_Completed method In Connect, connect id = " + theConnectingToken.TokenId);
                }

                ProcessConnect(e);
                break;

            case SocketAsyncOperation.Receive:
                if (Program.watchProgramFlow == true)       //for testing
                {
                    DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
                    Program.testWriter.WriteLine("\r\nIO_Completed method In Receive, id = " + receiveSendToken.TokenId);
                }

                ProcessReceive(e);
                break;

            case SocketAsyncOperation.Send:
                if (Program.watchProgramFlow == true)       //for testing
                {
                    DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
                    Program.testWriter.WriteLine("\r\nIO_Completed method In Send, id = " + receiveSendToken.TokenId);
                }

                ProcessSend(e);
                break;

            case SocketAsyncOperation.Disconnect:
                if (Program.watchProgramFlow == true)       //for testing
                {
                    DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
                    Program.testWriter.WriteLine("\r\nIO_Completed method In Disconnect, id = " + receiveSendToken.TokenId);
                }
                ProcessDisconnectAndCloseSocket(e);
                break;


            default:
            {
                DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
                if (Program.watchProgramFlow == true)           //for testing
                {
                    Program.testWriter.WriteLine("\r\nError in I/O Completed, id = " + receiveSendToken.TokenId);
                }

                throw new ArgumentException("\r\nError in I/O Completed, id = " + receiveSendToken.TokenId);
            }
            }
        }
Пример #4
0
        //____________________________________________________________________________
        // Pass the connection info from the connecting object to the object
        // that will do send/receive. And put the connecting object back in the pool.
        //
        private void ProcessConnect(SocketAsyncEventArgs connectEventArgs)
        {
            ConnectOpUserToken theConnectingToken = (ConnectOpUserToken)connectEventArgs.UserToken;

            if (connectEventArgs.SocketError == SocketError.Success)
            {
                lock (this.lockerForConnectionCount)
                {
                    this.clientsNowConnectedCount++;
                    if (this.clientsNowConnectedCount > this.maxSimultaneousClientsThatWereConnected)
                    {
                        this.maxSimultaneousClientsThatWereConnected++;
                    }
                }

                SocketAsyncEventArgs receiveSendEventArgs = this.poolOfRecSendEventArgs.Pop();
                receiveSendEventArgs.AcceptSocket = connectEventArgs.AcceptSocket;

                //Earlier, in the UserToken of connectEventArgs we put an array
                //of messages to send. Now we move that array to the DataHolder in
                //the UserToken of receiveSendEventArgs.
                DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
                receiveSendToken.theDataHolder.PutMessagesToSend(theConnectingToken.outgoingMessageHolder.arrayOfMessages);

                if (Program.showConnectAndDisconnect == true)
                {
                    Program.testWriter.WriteLine("ProcessConnect connect id " + theConnectingToken.TokenId + " socket info now passing to\r\n   sendReceive id " + receiveSendToken.TokenId + ", local endpoint = " + IPAddress.Parse(((IPEndPoint)connectEventArgs.AcceptSocket.LocalEndPoint).Address.ToString()) + ": " + ((IPEndPoint)connectEventArgs.AcceptSocket.LocalEndPoint).Port.ToString() + ". Clients connected to server from this machine = " + this.clientsNowConnectedCount);
                }

                messagePreparer.GetDataToSend(receiveSendEventArgs);
                StartSend(receiveSendEventArgs);

                //release connectEventArgs object back to the pool.
                connectEventArgs.AcceptSocket = null;
                this.poolOfConnectEventArgs.Push(connectEventArgs);

                if (Program.watchProgramFlow == true)   //for testing
                {
                    Program.testWriter.WriteLine("back to pool for connection object " + theConnectingToken.TokenId);
                }
            }

            //This else statement is when there was a socket error
            else
            {
                ProcessConnectionError(connectEventArgs);
            }
        }
Пример #5
0
        //____________________________________________________________________________
        // This method is called when we need to create a new SAEA object to do
        //connect operations. The reason to put it in a separate method is so that
        //we can easily add more objects to the pool if we need to.
        //You can do that if you do NOT use a buffer in the SAEA object that does
        //the connect operations.
        private SocketAsyncEventArgs CreateNewSaeaForConnect(SocketAsyncEventArgsPool pool)
        {
            //Allocate the SocketAsyncEventArgs object.
            SocketAsyncEventArgs connectEventArg = new SocketAsyncEventArgs();

            //Attach the event handler.  Since we'll be using this
            //SocketAsyncEventArgs object to process connect ops,
            //what this does is cause the calling of the ConnectEventArg_Completed
            //object when the connect op completes.
            connectEventArg.Completed += new EventHandler <SocketAsyncEventArgs>(IO_Completed);

            ConnectOpUserToken theConnectingToken = new ConnectOpUserToken(pool.AssignTokenId() + 10000);

            connectEventArg.UserToken = theConnectingToken;

            return(connectEventArg);

            //You may wonder about the buffer of this object. If you
            //decide to use objects from one pool to do connect operations, and
            //a separate pool of SAEA objects to do send/receive, then
            //there is NO NEED to assign a buffer to this SAEA object for connects.
            //But, if you want to
            //use this SAEA object to do connect, send, receive, and disconnect,
            //then you will need a buffer for this object.
            //Working with the buffer is different in the client vs the server, due
            //to the way that ConnectAsync works.
            //You would need to think about whether to do the initial call of
            //BufferManager.SetBuffer here, or do it in ProcessConnect method.
            //If a SocketAsyncEventArg object has a buffer already set before
            //the ConnectAsync method is called, then the contents of the buffer will
            //be sent immediately upon connecting, without your calling StartSend().
            //Read that sentence again.
            //So you could only call BufferManager.SetBuffer here if you are sure the
            //client will always do a send operation first, and the data will be
            //ready when the connection is made. If you want to have the
            //option of doing a receive operation first, then wait and set the buffer
            //after the connect operation completes.
            //If you decide to use that design, then you will need to call the
            //BufferManager's FreeBuffer method, to
            //null the buffer again before putting it back in the pool. FreeBuffer would
            //probably need to be called in the ProcessDisconnectAndCloseSocket method.
        }
Пример #6
0
        //____________________________________________________________________________
        // This method is called when we need to create a new SAEA object to do
        //connect operations. The reason to put it in a separate method is so that
        //we can easily add more objects to the pool if we need to.
        //You can do that if you do NOT use a buffer in the SAEA object that does
        //the connect operations.
        private SocketAsyncEventArgs CreateNewSaeaForConnect(SocketAsyncEventArgsPool pool)
        {
            //Allocate the SocketAsyncEventArgs object. 
            SocketAsyncEventArgs connectEventArg = new SocketAsyncEventArgs();            

            //Attach the event handler.  Since we'll be using this 
            //SocketAsyncEventArgs object to process connect ops,
            //what this does is cause the calling of the ConnectEventArg_Completed
            //object when the connect op completes.
            connectEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);

            ConnectOpUserToken theConnectingToken = new ConnectOpUserToken(pool.AssignTokenId() + 10000);
            connectEventArg.UserToken = theConnectingToken;

            return connectEventArg;

            //You may wonder about the buffer of this object. If you
            //decide to use objects from one pool to do connect operations, and
            //a separate pool of SAEA objects to do send/receive, then
            //there is NO NEED to assign a buffer to this SAEA object for connects.
            //But, if you want to 
            //use this SAEA object to do connect, send, receive, and disconnect,
            //then you will need a buffer for this object.
            //Working with the buffer is different in the client vs the server, due
            //to the way that ConnectAsync works.
            //You would need to think about whether to do the initial call of
            //BufferManager.SetBuffer here, or do it in ProcessConnect method.            
            //If a SocketAsyncEventArg object has a buffer already set before
            //the ConnectAsync method is called, then the contents of the buffer will 
            //be sent immediately upon connecting, without your calling StartSend().
            //Read that sentence again.
            //So you could only call BufferManager.SetBuffer here if you are sure the
            //client will always do a send operation first, and the data will be
            //ready when the connection is made. If you want to have the
            //option of doing a receive operation first, then wait and set the buffer
            //after the connect operation completes. 
            //If you decide to use that design, then you will need to call the 
            //BufferManager's FreeBuffer method, to
            //null the buffer again before putting it back in the pool. FreeBuffer would
            //probably need to be called in the ProcessDisconnectAndCloseSocket method. 
        }
Пример #7
0
        //____________________________________________________________________________
        internal void ProcessConnectionError(SocketAsyncEventArgs connectEventArgs)
        {
            ConnectOpUserToken theConnectingToken = (ConnectOpUserToken)connectEventArgs.UserToken;

            if (Program.watchProgramFlow == true)   //for testing
            {
                Program.testWriter.WriteLine("ProcessConnectionError() id = " + theConnectingToken.TokenId + ". ERROR: " + connectEventArgs.SocketError.ToString());
            }
            else if (Program.writeErrorsToLog == true)
            {
                Program.testWriter.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff") + " ProcessConnectionError() id = " + theConnectingToken.TokenId + ". ERROR: " + connectEventArgs.SocketError.ToString());
            }

            Interlocked.Increment(ref this.totalNumberOfConnectionRetries);

            // If connection was refused by server or timed out or not reachable, then we'll keep this socket.
            // If not, then we'll destroy it.
            if ((connectEventArgs.SocketError != SocketError.ConnectionRefused) && (connectEventArgs.SocketError != SocketError.TimedOut) && (connectEventArgs.SocketError != SocketError.HostUnreachable))
            {
                CloseSocket(connectEventArgs.AcceptSocket);
            }

            if (Program.continuallyRetryConnectIfSocketError == true)
            {
                // Since we did not send the messages, let's put them back in the stack.
                // We cannot leave them in the SAEA for connect ops, because the SAEA
                // could get pushed down in the stack and not reached.

                // If runLongTest == true, we reuse the same array of messages. So in that case
                // we do NOT need to put the array back in the BlockingStack.
                // But if runLongTest == false, we need to put the array of messages back in
                // the blocking stack, so that it will be taken out and sent.
                if (Program.runLongTest == true)
                {
                    this.counterForLongTest.Release();
                }
                else
                {
                    this.stackOfOutgoingMessages.Push(theConnectingToken.outgoingMessageHolder);
                    this.poolOfConnectEventArgs.Push(connectEventArgs);
                }
            }
            else
            {
                //it is time to release connectEventArgs object back to the pool.
                this.poolOfConnectEventArgs.Push(connectEventArgs);

                if (Program.watchProgramFlow == true)   //for testing
                {
                    Program.testWriter.WriteLine("back to pool for socket and SAEA " + theConnectingToken.TokenId);
                }

                Interlocked.Increment(ref this.totalNumberOfConnectionsFinished);
                //If we are not retrying the failed connections, then we need to
                //account for them here, when deciding whether we have finished
                //the test.
                if (this.totalNumberOfConnectionsFinished == this.socketClientSettings.ConnectionsToRun)
                {
                    FinishTest();
                }
            }
            this.theMaxConnectionsEnforcer.Release();
        }