private void ProcessDisconnectAndCloseSocket(SocketAsyncEventArgs receiveSendEventArgs)
        {
            try
            {
                ClientUserToken receiveSendToken = (ClientUserToken)receiveSendEventArgs.UserToken;

                //This method closes the socket and releases all resources, both
                //managed and unmanaged. It internally calls Dispose.
                receiveSendEventArgs.AcceptSocket.Close();

                //create an object that we can write data to.
                receiveSendToken.CreateNewSendDataHolder();

                receiveSendToken.isReuseConnection = false;

                // It is time to release this SAEA object.
                this.poolOfRecSendEventArgs.Push(receiveSendEventArgs);
            }
            catch (Exception shutdownErr)
            {
                LogManager.Log(string.Empty, shutdownErr);
            }
            finally
            {
                this.maxConcurrentConnection.Release();
                Interlocked.Decrement(ref currentConnectionCount);
                GetSimplePerf().PerfConcurrentClientConnectionCounter.Decrement();
            }
        }
        private void StartSend(SocketAsyncEventArgs receiveSendEventArgs)
        {
            ClientUserToken receiveSendToken = (ClientUserToken)receiveSendEventArgs.UserToken;

            if (receiveSendToken.sendBytesRemainingCount <= this.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.sendBytesRemaining > its size, we just
                //set it to the maximum size, to send the most data possible.
                receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, this.bufferSize);
                //Copy the bytes to the buffer associated with this SAEA object.
                Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, this.bufferSize);

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

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

            if (!willRaiseEvent)
            {
                ProcessSend(receiveSendEventArgs);
            }
        }
Example #3
0
        internal static void GetDataToSend(SocketAsyncEventArgs e)
        {
            ClientUserToken theUserToken = (ClientUserToken)e.UserToken;
            SendDataHolder  dataHolder   = theUserToken.sendDataHolder;

            //In this example code, we will
            //prefix the message with the length of the message. So we put 2
            //things into the array.
            // 1) prefix,
            // 2) the message.

            //Determine the length of the message that we will send.
            int lengthOfCurrentOutgoingMessage = dataHolder.ArrayOfMessageToSend[dataHolder.NumberOfMessageHadSent].Content.Length;

            //convert the message to byte array
            byte[] arrayOfBytesInMessage = dataHolder.ArrayOfMessageToSend[dataHolder.NumberOfMessageHadSent].Content;

            //So, now we convert the length integer into a byte array.
            //Aren't byte arrays wonderful? Maybe you'll dream about byte arrays tonight!
            byte[] arrayOfBytesInPrefix = BitConverter.GetBytes(lengthOfCurrentOutgoingMessage);

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

            theUserToken.messageTokenId = dataHolder.ArrayOfMessageToSend[dataHolder.NumberOfMessageHadSent].TokenId;

            //Now copy the 2 things to the theUserToken.dataToSend.
            Buffer.BlockCopy(arrayOfBytesInPrefix, 0, theUserToken.dataToSend, 0, theUserToken.sendPrefixLength);
            Buffer.BlockCopy(arrayOfBytesInMessage, 0, theUserToken.dataToSend, theUserToken.sendPrefixLength, lengthOfCurrentOutgoingMessage);

            theUserToken.sendBytesRemainingCount = theUserToken.sendPrefixLength + lengthOfCurrentOutgoingMessage;
            theUserToken.bytesSentAlreadyCount   = 0;
        }
        private bool ReuseHeartBeatSAEA(List <Message> messages, string serverEndPointKey)
        {
            try {
                if (!dictPoolOfHeartBeatRecSendEventArgs.ContainsKey(serverEndPointKey))
                {
                    return(false);
                }

                SocketAsyncEventArgPool thisPortSocketPool = dictPoolOfHeartBeatRecSendEventArgs[serverEndPointKey];

                //while (!thisPortSocketPool.IsEmpty)
                while (true)
                {
                    cleanSignal.Reset();

                    //鉴于IsEmpty有问题,确保有Pop尝试!
                    SocketAsyncEventArgs arg = thisPortSocketPool.Pop();

                    if (arg == null)
                    {
                        if (thisPortSocketPool.IsEmpty)
                        {
                            LogManager.LogTraceInfo(string.Format("poolKey:{0} 's pool empty now with count:{1}", serverEndPointKey, thisPortSocketPool.Count));
                            return(false);
                        }
                        else
                        {
                            Thread.Sleep(1);
                            continue;
                        }
                    }

                    GetSimplePerf().PerfClientReuseConnectionCounter.Increment();
                    GetSimplePerf().PerfClientIdleConnectionCounter.Decrement();

                    ClientUserToken userToken = (ClientUserToken)arg.UserToken;
                    userToken.CreateNewSendDataHolder();
                    SendDataHolder sendDataHodler = userToken.sendDataHolder;

                    sendDataHodler.SetSendMessage(messages);
                    MessagePreparer.GetDataToSend(arg);
                    sendDataHodler.ArrayOfMessageToSend[0].StartTime = DateTime.Now;

                    StartSend(arg);

                    return(true);
                }
            }
            catch (Exception pickErr) {
                LogManager.Log(string.Format("pick socket from poolKey:{0} occur error!", serverEndPointKey), pickErr);
            }
            finally {
                cleanSignal.Set();
            }

            return(false);
        }
        private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs)
        {
            ClientUserToken receiveSendToken = (ClientUserToken)receiveSendEventArgs.UserToken;

            receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetReceive, this.bufferSize);

            bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs);

            if (!willRaiseEvent)
            {
                ProcessReceive(receiveSendEventArgs);
            }
        }
        private void ProcessSend(SocketAsyncEventArgs receiveSendEventArgs)
        {
            ClientUserToken receiveSendToken = (ClientUserToken)receiveSendEventArgs.UserToken;

            if (receiveSendEventArgs.SocketError == SocketError.Success)
            {
                receiveSendToken.sendBytesRemainingCount = receiveSendToken.sendBytesRemainingCount - receiveSendEventArgs.BytesTransferred;
                // If this if statement is true, then we have sent all of the
                // bytes in the message. Otherwise, at least one more send
                // operation will be required to send the data.
                if (receiveSendToken.sendBytesRemainingCount == 0)
                {
                    //incrementing count of messages sent on this connection
                    receiveSendToken.sendDataHolder.NumberOfMessageHadSent++;

                    //准备接受返回数据
                    StartReceive(receiveSendEventArgs);
                }
                else
                {
                    // So since (receiveSendToken.sendBytesRemaining == 0) is false,
                    // we have more bytes to send for this message. So we need to
                    // call StartSend, so we can post another send message.
                    receiveSendToken.bytesSentAlreadyCount += receiveSendEventArgs.BytesTransferred;
                    StartSend(receiveSendEventArgs);
                }
            }
            else
            {
                string StatusErrorInfo = receiveSendEventArgs.SocketError.ToString();
                receiveSendToken.Reset();
                StartDisconnect(receiveSendEventArgs);

                ReceiveFeedbackDataCompleteEventArg arg = new ReceiveFeedbackDataCompleteEventArg();
                arg.MessageTokenId = receiveSendToken.messageTokenId;
                arg.FeedbackData   = null;

                LogManager.Log("发送数据到服务端失败!", new Exception(string.Format("FeedbackError:messageTokenId:{0}因{1}而主动设置为NULL", arg.MessageTokenId, StatusErrorInfo)));

                if (ReceiveFeedbackDataComplete != null)
                {
                    ReceiveFeedbackDataComplete(this, arg);
                }
            }
        }
        private void ProcessConnect(SocketAsyncEventArgs connectEventArgs)
        {
            ConnectOpUserToken theConnectingToken = (ConnectOpUserToken)connectEventArgs.UserToken;

            if (connectEventArgs.SocketError == SocketError.Success)
            {
                SocketAsyncEventArgs receiveSendEventArgs = poolOfRecSendEventArgs.Pop();

                if (receiveSendEventArgs == null)
                {
                    //几乎不可能发生!
                    LogManager.Log(string.Empty, new Exception("fetch receiveSendEventArgs failed for connect"));
                    return;
                }

                Interlocked.Increment(ref currentConnectionCount);
                GetSimplePerf().PerfConcurrentClientConnectionCounter.Increment();

                receiveSendEventArgs.AcceptSocket = connectEventArgs.AcceptSocket;

                ClientUserToken receiveSendToken = (ClientUserToken)receiveSendEventArgs.UserToken;

                receiveSendToken.sendDataHolder.SetSendMessage(theConnectingToken.ArrayOfMessageReadyToSend);
                receiveSendToken.ServerEndPointKey = theConnectingToken.ServerEndPointKey;

                MessagePreparer.GetDataToSend(receiveSendEventArgs);

                receiveSendToken.startTime = theConnectingToken.ArrayOfMessageReadyToSend[0].StartTime;

                StartSend(receiveSendEventArgs);

                //release connectEventArgs object back to the pool.
                connectEventArgs.AcceptSocket = null;
                this.poolOfConnectEventArgs.Push(connectEventArgs);
            }
            else
            {
                ProcessConnectionError(connectEventArgs);
            }
        }
        internal 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:

                ProcessConnect(e);
                break;

            case SocketAsyncOperation.Receive:

                ProcessReceive(e);
                break;

            case SocketAsyncOperation.Send:

                ProcessSend(e);
                break;

            case SocketAsyncOperation.Disconnect:

                ProcessDisconnectAndCloseSocket(e);
                break;

            default:
            {
                ClientUserToken receiveSendToken = (ClientUserToken)e.UserToken;
                if (ReceiveFeedbackDataComplete != null)
                {
                    ReceiveFeedbackDataComplete(receiveSendToken.messageTokenId, null);
                }

                this.maxConcurrentConnection.Release();

                LogManager.Log(string.Empty, new ArgumentException("\r\nFeedbackError:Error in I/O Completed,返回数据主动设置为NULL, LastOperation: " + e.LastOperation));
            }
            break;
            }
        }
        private void SetToHeartBeatStatus(SocketAsyncEventArgs e, ClientUserToken userToken)
        {
            //加入support keepalive机制处理逻辑
            userToken.startTime = DateTime.Now;
            if (!userToken.isReuseConnection)
            {
                userToken.isReuseConnection = true;
            }
            else
            {
                GetSimplePerf().PerfClientReuseConnectionCounter.Decrement();
            }

            GetSimplePerf().PerfClientIdleConnectionCounter.Increment();

            if (!dictPoolOfHeartBeatRecSendEventArgs.ContainsKey(userToken.ServerEndPointKey))
            {
                lock (this)
                {
                    if (!dictPoolOfHeartBeatRecSendEventArgs.ContainsKey(userToken.ServerEndPointKey))
                    {
                        SocketAsyncEventArgPool pool = new SocketAsyncEventArgPool();
                        pool.Push(e);
                        dictPoolOfHeartBeatRecSendEventArgs.Add(userToken.ServerEndPointKey, pool);
                    }
                    else
                    {
                        dictPoolOfHeartBeatRecSendEventArgs[userToken.ServerEndPointKey].Push(e);
                    }
                }
            }
            else
            {
                dictPoolOfHeartBeatRecSendEventArgs[userToken.ServerEndPointKey].Push(e);
            }
        }
        private void RunHeartBeat()
        {
            int  waitTime       = 180000;
            bool alreadyChecked = false;

            while (true)
            {
                if (alreadyChecked)
                {
                    if (!DateTime.Now.Hour.Equals(3))
                    {
                        alreadyChecked = false;
                    }

                    Thread.Sleep(waitTime);
                }

                if (DateTime.Now.Equals(3))
                {
                    foreach (string poolKey in dictPoolOfHeartBeatRecSendEventArgs.Keys)
                    {
                        SocketAsyncEventArgPool thisPortKey = dictPoolOfHeartBeatRecSendEventArgs[poolKey];

                        if (thisPortKey.IsEmpty)
                        {
                            continue;
                        }

                        Stopwatch sw = new Stopwatch();

                        sw.Start();
                        bool existNeedReuseItem = false;

                        cleanSignal.WaitOne();

                        SocketAsyncEventArgs        heartBeatSAEA = thisPortKey.Pop();
                        List <SocketAsyncEventArgs> listRepush    = new List <SocketAsyncEventArgs>();

                        while (!thisPortKey.IsEmpty)
                        {
                            if (heartBeatSAEA != null)
                            {
                                ClientUserToken userToken = (ClientUserToken)heartBeatSAEA.UserToken;

                                if (DateTime.Now.Subtract(userToken.startTime).TotalSeconds < 120)
                                {
                                    listRepush.Add(heartBeatSAEA);
                                    existNeedReuseItem = true;
                                }
                                else
                                {
                                    //说明太闲了(完全空闲两分钟了!一直没被Pop出去复用),不用发所谓心跳,直接关闭
                                    StartDisconnect(heartBeatSAEA);
                                    GetSimplePerf().PerfClientIdleConnectionCounter.Decrement();
                                }
                            }

                            if (existNeedReuseItem)
                            {
                                //别因为等待信号,导致可复用连接长时间无效闲置
                                if (sw.ElapsedMilliseconds > 100)
                                {
                                    break;
                                }
                            }

                            cleanSignal.WaitOne();
                            heartBeatSAEA = thisPortKey.Pop();
                        }

                        for (int i = 0; i < listRepush.Count; i++)
                        {
                            thisPortKey.Push(listRepush[i]);
                        }

                        //if (listRepush.Count > 1)
                        //    thisPortKey.BatchPush(listRepush.ToArray());
                        //else if (listRepush.Count.Equals(1))
                        //    thisPortKey.Push(listRepush[0]);
                    }

                    alreadyChecked = true;
                }

                Thread.Sleep(waitTime);
            }
        }
        private void ProcessReceive(SocketAsyncEventArgs receiveSendEventArgs)
        {
            ClientUserToken receiveSendToken = (ClientUserToken)receiveSendEventArgs.UserToken;

            if (receiveSendEventArgs.SocketError != SocketError.Success)
            {
                LogManager.Log(string.Format("SocketError:{0} on {1}", receiveSendEventArgs.SocketError, receiveSendEventArgs.AcceptSocket.RemoteEndPoint), new ArgumentException("SocketError"));
                receiveSendToken.Reset();
                StartDisconnect(receiveSendEventArgs);
                return;
            }

            if (receiveSendEventArgs.BytesTransferred == 0)
            {
                LogManager.Log(string.Format("SocketError:{0} on {1} for byte transfer equal zero", receiveSendEventArgs.SocketError, receiveSendEventArgs.AcceptSocket.RemoteEndPoint), new ArgumentException("BytesTransferred"));
                receiveSendToken.Reset();
                StartDisconnect(receiveSendEventArgs);
                return;
            }

            int remainingBytesToProcess = receiveSendEventArgs.BytesTransferred;


            // If we have not got all of the prefix then we need to work on it.
            // receivedPrefixBytesDoneCount tells us how many prefix bytes were
            // processed during previous receive ops which contained data for
            // this message. (In normal use, usually there will NOT have been any
            // previous receive ops here. So receivedPrefixBytesDoneCount would be 0.)
            if (receiveSendToken.receivedPrefixBytesDoneCount < this.prefixHandleLength)
            {
                bool getLengthInfoSuccessfully = false;

                remainingBytesToProcess = PrefixHandler.HandlePrefix(receiveSendEventArgs, receiveSendToken, remainingBytesToProcess, ref getLengthInfoSuccessfully);

                if (remainingBytesToProcess == 0)
                {
                    // We need to do another receive op, since we do not have
                    // the message yet.
                    StartReceive(receiveSendEventArgs);

                    //Jump out of the method, since there is no more data.
                    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 (ReceiveFeedbackDataComplete != null)
                {
                    ReceiveFeedbackDataCompleteEventArg arg = new ReceiveFeedbackDataCompleteEventArg();
                    arg.MessageTokenId = receiveSendToken.messageTokenId;
                    arg.FeedbackData   = receiveSendToken.dataMessageReceived;

                    if (arg.FeedbackData == null)
                    {
                        LogManager.Log("服务端返回数据为NULL!", new ArgumentException(string.Format("FeedbackError:arg.FeedbackData on {0}", arg.MessageTokenId)));
                    }

                    ReceiveFeedbackDataComplete(this, arg);
                }

                receiveSendToken.Reset();

                //receiveSendToken.sendDataHolder.NumberOfMessageHadSent在ProcessSend成功后修正加一
                //这里是等待回传数据后,开始下次发送前的判断,直至将List<Message>全部发送完毕为止,
                //List<Message>中的数量由调用方控制
                if (receiveSendToken.sendDataHolder.ArrayOfMessageToSend.Count > receiveSendToken.sendDataHolder.NumberOfMessageHadSent)
                {
                    MessagePreparer.GetDataToSend(receiveSendEventArgs);
                    StartSend(receiveSendEventArgs);
                }
                else
                {
                    if (!supportKeepAlive)
                    {
                        receiveSendToken.sendDataHolder.ArrayOfMessageToSend = null;
                        //disconnect中会重新new一个senddataholder,numberofmessagehadsent即清零
                        StartDisconnect(receiveSendEventArgs);
                    }
                    else
                    {
                        //加入support keepalive机制处理逻辑
                        SetToHeartBeatStatus(receiveSendEventArgs, receiveSendToken);
                    }
                }
            }
            else
            {
                receiveSendToken.receiveMessageOffset     = receiveSendToken.bufferOffsetReceive;
                receiveSendToken.recPrefixBytesDoneThisOp = 0;

                StartReceive(receiveSendEventArgs);
            }
        }
        private void Init()
        {
            if (!isInit)
            {
                lock (lockForPickObject)
                {
                    if (!isInit)
                    {
                        Stopwatch sw = new Stopwatch();

                        sw.Start();

                        clientSetting = GetLocalClientSetting();

                        ServerCount = clientSetting.serverEndPoints.Length;

                        timeOutByMS = clientSetting.timeOutByMS;

                        //初始化后,不可更改!
                        bufferManager = new BufferManager(clientSetting.bufferSize * clientSetting.opsToPreAllocate * clientSetting.numberOfSaeaForRecSend, clientSetting.bufferSize * clientSetting.opsToPreAllocate);
                        bufferManager.InitBuffer();

                        //用于负责建立连接的saea,无关buffermanager,10个足够!这部分实际在ClientSocketProcessor中可以动态增加
                        //poolOfConnectEventArgs = new SocketAsyncEventArgPool(clientSetting.maxSimultaneousConnectOps);
                        poolOfConnectEventArgs = new SocketAsyncEventArgPool();

                        //用于负责在建立好的连接上传输数据,涉及buffermanager,目前测试100~200个足够!这部分目前不支持动态增加!
                        //因其buffermanager是事先分配好的一大块连续的固定内存区域,强烈建议不再更改,需要做好的就是事先的大小评估。
                        //poolOfRecSendEventArgs = new SocketAsyncEventArgPool(clientSetting.numberOfSaeaForRecSend);
                        poolOfRecSendEventArgs = new SocketAsyncEventArgPool();

                        //实际负责处理相关传输数据的关键核心类
                        processor = new ClientSocketProcessor(poolOfConnectEventArgs, poolOfRecSendEventArgs, clientSetting.numberOfSaeaForRecSend, clientSetting.bufferSize, clientSetting.numberOfMessagesPerConnection, clientSetting.receivePrefixLength, clientSetting.useKeepAlive, "bizclient");

                        //由于不涉及buffermanager,可动态增长
                        for (int i = 0; i < clientSetting.maxSimultaneousConnectOps; i++)
                        {
                            SocketAsyncEventArgs connectEventArg = new SocketAsyncEventArgs();
                            connectEventArg.Completed += new EventHandler <SocketAsyncEventArgs>(processor.IO_Completed);

                            //关键负责标识saea和更关键的传输待发送的数据给传输用的saea。
                            ConnectOpUserToken theConnectingToken = new ConnectOpUserToken();
                            connectEventArg.UserToken = theConnectingToken;

                            poolOfConnectEventArgs.Push(connectEventArg);
                        }

                        //涉及buffermanager,不可动态增长,需事先评估即可,负责实际的数据传输
                        for (int i = 0; i < clientSetting.numberOfSaeaForRecSend; i++)
                        {
                            SocketAsyncEventArgs eventArgObjectForPool = new SocketAsyncEventArgs();

                            //事先为每个saea分配固定不变的内存位置!
                            bufferManager.SetBuffer(eventArgObjectForPool);

                            eventArgObjectForPool.Completed += new EventHandler <SocketAsyncEventArgs>(processor.IO_Completed);

                            ClientUserToken receiveSendToken = new ClientUserToken(eventArgObjectForPool.Offset, eventArgObjectForPool.Offset + clientSetting.bufferSize, clientSetting.receivePrefixLength, clientSetting.sendPrefixLength);

                            //用于传递待发送的数据,一旦完成发送必须重新new一个。
                            receiveSendToken.CreateNewSendDataHolder();
                            eventArgObjectForPool.UserToken = receiveSendToken;

                            poolOfRecSendEventArgs.Push(eventArgObjectForPool);
                        }

                        bufferManager.SetInitComplete();

                        sw.Stop();

                        LogManager.Log(string.Format("SocketClient Init by FirstInvoke Completed! ConsumeTime:{0} ms", sw.ElapsedMilliseconds));

                        isInit = true;
                    }
                }
            }
        }