private void PipeRemoteReceiveCallback(IAsyncResult ar)
 {
     if (_closed)
     {
         return;
     }
     try
     {
         var session   = (AsyncSession)ar.AsyncState;
         int bytesRead = session.Remote.EndReceive(ar);
         _totalRead += bytesRead;
         _tcprelay.UpdateInboundCounter(_server, bytesRead);
         if (bytesRead > 0)
         {
             lastActivity = DateTime.Now;
             int bytesToSend = -1;
             lock (_decryptionLock)
             {
                 try
                 {
                     _encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
                 }
                 catch (CryptoErrorException)
                 {
                     Logging.Error("decryption error");
                     Close();
                     return;
                 }
             }
             if (bytesToSend == 0)
             {
                 // need more to decrypt
                 Logging.Debug("Need more to decrypt");
                 session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None,
                                             PipeRemoteReceiveCallback, session);
                 return;
             }
             Logging.Debug($"start sending {bytesToSend}");
             _connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None,
                                   PipeConnectionSendCallback, new object[] { session, bytesToSend });
             IStrategy strategy = _controller.GetCurrentStrategy();
             strategy?.UpdateLastRead(_server);
         }
         else
         {
             _connection.Shutdown(SocketShutdown.Send);
             _connectionShutdown = true;
             CheckClose();
         }
     }
     catch (Exception e)
     {
         Logging.LogUsefulException(e);
         Close();
     }
 }
 private void PipeRemoteReceiveCallback(IAsyncResult ar)
 {
     if (_closed)
     {
         return;
     }
     try
     {
         int bytesRead = _remote.EndReceive(ar);
         _totalRead += bytesRead;
         _tcprelay.UpdateInboundCounter(_server, bytesRead);
         if (bytesRead > 0)
         {
             lastActivity = DateTime.Now;
             int bytesToSend = -1;
             lock (_decryptionLock)
             {
                 try
                 {
                     _encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
                 }
                 catch (CryptoErrorException e)
                 {
                     Logging.LogUsefulException(e);
                     Close();
                     return;
                 }
             }
             if (bytesToSend == 0)
             {
                 // need more to decrypt
                 Logging.Debug("Need more to decrypt");
                 _remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None,
                                      PipeRemoteReceiveCallback, null);
                 return;
             }
             Logging.Debug($"start sending {bytesToSend}");
             _connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None,
                                   PipeConnectionSendCallback, bytesToSend);
         }
         else
         {
             _connection.Shutdown(SocketShutdown.Send);
             _connectionShutdown = true;
             CheckClose();
         }
     }
     catch (Exception e)
     {
         Logging.LogUsefulException(e);
         Close();
     }
 }
        // server recv -> local send
        private async Task Downstream()
        {
            SaeaAwaitable serverRecvSaea = null;
            SaeaAwaitable localSendSaea  = null;

            try
            {
                while (IsRunning)
                {
                    serverRecvSaea = _argsPool.Rent();
                    var token = await _serverSocket.FullReceiveTaskAsync(serverRecvSaea, TCPRelay.RecvSize);

                    var err         = token.SocketError;
                    var bytesRecved = token.BytesTotalTransferred;
                    Logging.Debug($"Downstream server recv: {err},{bytesRecved}");

                    if (err == SocketError.Success && bytesRecved <= 0)
                    {
                        _localSocket.Shutdown(SocketShutdown.Send);
                        _localShutdown = true;
                        CheckClose();
                        return;
                    }
                    if (err != SocketError.Success)
                    {
                        Logging.Debug($"Downstream server recv socket err: {err}");
                        Close();
                        return;
                    }
                    Debug.Assert(bytesRecved <= TCPRelay.RecvSize);
                    _tcprelay.UpdateInboundCounter(_server, bytesRecved);
                    lastActivity = DateTime.Now;

                    localSendSaea = _argsPool.Rent();
                    int decBufLen = -1;
                    lock (_decryptionLock)
                    {
                        _encryptor.Decrypt(serverRecvSaea.Saea.Buffer,
                                           bytesRecved,
                                           localSendSaea.Saea.Buffer,
                                           out decBufLen);
                    }
                    _argsPool.Return(serverRecvSaea);
                    serverRecvSaea = null;

                    token = await _localSocket.FullSendTaskAsync(localSendSaea, decBufLen);

                    err = token.SocketError;
                    var bytesSent = token.BytesTotalTransferred;
                    Logging.Debug($"Downstream local send socket err: {err},{bytesSent}");
                    if (err != SocketError.Success)
                    {
                        Close();
                        return;
                    }
                    _argsPool.Return(localSendSaea);
                    localSendSaea = null;
                    Debug.Assert(bytesSent == decBufLen);
                }
            }
            catch (AggregateException agex)
            {
                foreach (var ex in agex.InnerExceptions)
                {
                    Logging.LogUsefulException(ex);
                }
                Close();
            }
            catch (Exception e)
            {
                Logging.LogUsefulException(e);
                Close();
            }
            finally
            {
                _argsPool.Return(serverRecvSaea);
                serverRecvSaea = null;
                _argsPool.Return(localSendSaea);
                localSendSaea = null;
            }
        }