【问题标题】:Cause a connected socket to accept new messages right after .BeginReceive?导致连接的套接字在 .BeginReceive 之后立即接受新消息?
【发布时间】:2012-12-04 18:46:44
【问题描述】:

基本上我已经建立了一个套接字连接。这是我的 WaitData 方法:

    public void WaitForData()
    {
        State state = new State();
        state.Client = socket;
        //issue first receive
        state.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), state);
    }

每当我的套接字等待数据时调用它(我希望它一直等待数据)。 因此,每当开始接收新消息(调用 .BeginReceive)时,我希望我的主线程再次调用 WaitForData 以便它继续接受消息。 这将如何实现?

(顺便说一句,我不能在 ServerReadCallback 中调用 WaitForData,因为它会阻止实际接收消息)。

对不起,如果我的描述很混乱;我觉得很难描述。

【问题讨论】:

    标签: c# vb.net multithreading visual-studio-2010 sockets


    【解决方案1】:

    在处理完消息后,在 ServerReadCallBack 例程中发出另一个 BeginReceive 即:

        State state = new State();
        state.Client = socket;
        //issue first receive
        state.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), state);
    

    但是请记住,如果您的消息是可变长度的,那么在缓冲区中读取的数据可能是消息的一部分;整个消息;或整个消息加上另一条消息的部分/全部。因此,您的 ServerReadCallback 需要能够处理这个问题 - 类似于:

        private void OnDataReceived(IAsyncResult asyn)
        {
            ReceiveState rState = (ReceiveState)asyn.AsyncState;
            Socket client = rState.Client;
            SocketError socketError = SocketError.TypeNotFound;
    
            if (!client.Connected)
            {
                // Not Connected anymore ?
                return;
            }
    
            _LastComms = DateTime.Now;
            _LastIn = _LastComms;
    
            int dataOffset = 0; 
            int restOfData = 0;
            int dataRead = 0;
            Boolean StreamClosed = false;
            long rStateDataLength = 0;
            long LastrStateDataLength = 0;
    
            try
            {
    
                dataRead = client.EndReceive(asyn, out socketError);
            }
            catch (Exception excpt)
            {
               // Your code goes here..
            }
    
            if (socketError != SocketError.Success)
            {
                // Has Connection been lost ?
                OnConnectionDropped(client);
                return;
            }
    
            if (dataRead <= 0)
            {
                // Has connection been lost ?
                OnConnectionDropped(client);
                return;
            }
    
            while (dataRead > 0)
            {
                //check to determine what income data contain: size prefix or message
                if (!rState.DataSizeReceived)
                {
                    //there is already some data in the buffer
                    if (rState.Data.Length > 0)
                    {
                        restOfData = PrefixSize - (int)rState.Data.Length;
                        rState.Data.Write(rState.Buffer, dataOffset, restOfData);
                        dataRead -= restOfData;
                        dataOffset += restOfData;
                    }
                    else if (dataRead >= PrefixSize)
                    {  //store whole data size prefix
                        rState.Data.Write(rState.Buffer, dataOffset, PrefixSize);
                        dataRead -= PrefixSize;
                        dataOffset += PrefixSize;
                    }
                    else
                    {  // store only part of the size prefix
                        rState.Data.Write(rState.Buffer, dataOffset, dataRead);
                        dataOffset += dataRead;
                        dataRead = 0;
                    }
    
                    if (rState.Data.Length == PrefixSize)
                    {  //we received data size prefix 
                        rState.DataSize = BitConverter.ToInt32(rState.Data.GetBuffer(), 0);
                        rState.DataSizeReceived = true;
                        //reset internal data stream             
                        rState.Data.Position = 0;
                        rState.Data.SetLength(0);
                    }
                    else
                    {  //we received just part of the prefix information 
                        //issue another read
                        client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                           SocketFlags.None, new AsyncCallback(socketReadCallBack),
                              rState);
                        return;
                    }
                }
    
    
    
                //at this point we know the size of the pending data
    
                // Object disposed exception may raise here
                try
                {
                    rStateDataLength = rState.Data.Length;
                    LastrStateDataLength = rStateDataLength;
                }
                catch (ObjectDisposedException Ode)
                {
                    StreamClosed = true;
                }
                if (!StreamClosed)
                {
    
                    if ((rStateDataLength + dataRead) >= rState.DataSize)
                    {   //we have all the data for this message
    
                        restOfData = rState.DataSize - (int)rState.Data.Length;
    
                        rState.Data.Write(rState.Buffer, dataOffset, restOfData);
                        //Console.WriteLine("Data message received. Size: {0}",
                        //   rState.DataSize);
    
                        // Is this a heartbeat message ?
                        // This is my own thing - you may not need this..
                        if (rState.Data.Length == 2)
                        {
                            // Yes
                            HeartBeatReceived();
                        }
                        else
    
                        {
                           // Handle the received messsage
    
                            DecodeMessageReceived(GetStringFromStream(rState.Data));
                        }
    
                        dataOffset += restOfData;
                        dataRead -= restOfData;
    
                        //message received - cleanup internal memory stream
                        rState.Data = new MemoryStream();
                        rState.Data.Position = 0;
                        rState.DataSizeReceived = false;
                        rState.DataSize = 0;
    
                        if (dataRead == 0)
                        {  
                            //no more data remaining to process - issue another receive
                            if (_IsConnected)
                            {
                                client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                                   SocketFlags.None, new AsyncCallback(socketReadCallBack),
                                      rState);
                                return;
                            }
                        }
                        else
                            continue; //there's still some data to process in the buffers
                    }
                    else
                    {  //there is still data pending, store what we've 
                        //received and issue another BeginReceive
                        if (_IsConnected)
                        {
                            rState.Data.Write(rState.Buffer, dataOffset, dataRead);
    
                            client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                               SocketFlags.None, new AsyncCallback(socketReadCallBack), rState);
    
                            dataRead = 0;
                        }
                    }
                }
                else
                {
                    // Stream closed, but have we read everything ?
                    if (LastrStateDataLength + dataRead == rState.DataSize)
                    {
                        // We're equal, get ready for more
                        //no more data remaining to process - issue another receive
                        if (_IsConnected)
                        {
                            client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                               SocketFlags.None, new AsyncCallback(socketReadCallBack),
                                  rState);
                        }
                        return;
                    }
                    else
                    {
                        // We should have more..
                        // report error ?
                    }
                }
    
                // If we've been disconnected, provide a graceful exit
                if (!_IsConnected)
                    dataRead = -1;
    
            }
         }
    

    在这段代码中,我的状态是:

    public class ReceiveState
    {
        public byte[] Buffer = new byte[1024]; //buffer for network i/o
        public int DataSize = 0; //data size to be received by the server
        public bool DataSizeReceived = false; //whether prefix was received
        public MemoryStream Data = new MemoryStream(); //place where data is stored
        public Socket Client;   //client socket
    }
    

    【讨论】:

      【解决方案2】:

      你想做这样的事情:

      private ManualResetEvent _connectionAccepted;
      
      public void WaitForData()
      {
          while (true)
          {
              _connectionAccepted.Reset();
      
              socket.BeginAccept(new AsyncCallback(AcceptCallback), state);
      
              _connectionAccepted.WaitOne();
          }
      }
      
      private void Acceptcallback(IAsyncResult ar)
      {
           _connectionAccepted.Set();
      
           Socket socket = (Socket)ar.AsyncState;
           Socket handler = socket.EndAccept(ar);
      
           State state = new State();
           state.Client = handler;
      
           handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), handler);
      }
      

      所以,基本上,你是无限循环的。当你得到一个连接时,你开始接受它;当您在处理接受连接的代码中时,您向主循环发出信号,表明它可以继续进行。然后你 EndAccept,它会为你提供第二个套接字 - 该套接字用于与客户端通信,从而允许初始套接字继续接受新连接。

      只需要一次调用 WaitForData() - 这就是循环所在的位置 - 因此您可能希望将其提升到命名或诸如此类的级别。但这是大意。

      【讨论】:

      • 我认为您在这里解决的是能够接受来自不同客户端的新连接;而不是继续从同一个客户端接收消息。我可能是错的......
      • @andrew:你是 100% 正确的。一定是疯了。如果他的兴趣是继续从一个连接接收,那么您已经在下面介绍了它。
      猜你喜欢
      • 1970-01-01
      • 2016-03-28
      • 2019-12-11
      • 2013-07-28
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-16
      相关资源
      最近更新 更多