Socket.BeginReceive/EndReceive函数以什么顺序调用?

例如,我两次调用 BeginReceive ,一次获取消息长度,第二次获取消息本身。现在的场景是这样的,对于我发送的每条消息,我开始等待消息的完成(实际上是对已发送消息的确认,在收到确认后我也要等待操作的完成),所以我将 BeginSend与每个 BeginSend一起调用 BeginReceive ,但是在每个 BeginReceive 的回调中,我检查是否接收到长度或消息。如果我已收到该消息并已完全接收到该消息,则我将调用另一个 BeginReceive 以接收该操作的完成。现在这是事情不同步的地方。因为我的接收回调之一正在接收字节,它实际上解释为消息本身,因此将其解释为消息的长度。

现在我该如何解决?

编辑:这是一个C#.NET问题:)

这是代码,基本上太大了,对此感到抱歉

public void Send(string message)
{
    try
    {
        bytesSent = 0;

        writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
        writeDataBuffer = WrapMessage(writeDataBuffer);
        messageSendSize = writeDataBuffer.Length;

        clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
                            new AsyncCallback(SendComplete), clientSocket);
    }
    catch (SocketException socketException)
    {
        MessageBox.Show(socketException.Message);
    }
}

public void WaitForData()
{
    try
    {
        if (!messageLengthReceived)
        {
            clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
                                    SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
        }
}

public void Send(string message)
{
    try
    {
        bytesSent = 0;

        writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
        writeDataBuffer = WrapMessage(writeDataBuffer);
        messageSendSize = writeDataBuffer.Length;

        clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
                            new AsyncCallback(SendComplete), clientSocket);
    }
    catch (SocketException socketException)
    {
        MessageBox.Show(socketException.Message);
    }
}

public void WaitForData()
{
    try
    {
        if (! messageLengthReceived)
        {
            clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
                                    SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
        }
        else
        {
            clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, messageLength - bytesReceived,
                                    SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
        }
    }
    catch (SocketException socketException)
    {
        MessageBox.Show(socketException.Message);
    }
}

public void RecieveComplete(IAsyncResult result)
{
    try
    {
        Socket socket = result.AsyncState as Socket;
        bytesReceived = socket.EndReceive(result);

        if (! messageLengthReceived)
        {
            if (bytesReceived != MESSAGE_LENGTH_SIZE)
            {
                WaitForData();
                return;
            }

            // unwrap message length
            int length = BitConverter.ToInt32(receiveDataBuffer, 0);
            length = IPAddress.NetworkToHostOrder(length);

            messageLength = length;
            messageLengthReceived = true;

            bytesReceived = 0;

            // now wait for getting the message itself
            WaitForData();
        }
        else
        {
            if (bytesReceived != messageLength)
            {
                WaitForData();
            }
            else
            {
                string message = Encoding.ASCII.GetString(receiveDataBuffer);

                MessageBox.Show(message);

                bytesReceived = 0;
                messageLengthReceived = false;

                // clear buffer
                receiveDataBuffer = new byte[AsyncClient.BUFFER_SIZE];

                WaitForData();
            }
        }
    }
    catch (SocketException socketException)
    {
        MessageBox.Show(socketException.Message);
    }

}

public void SendComplete(IAsyncResult result)
{
    try
    {
        Socket socket = result.AsyncState as Socket;
        bytesSent = socket.EndSend(result);

        if (bytesSent != messageSendSize)
        {
            messageSendSize -= bytesSent;

            socket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
                            new AsyncCallback(SendComplete), clientSocket);
            return;
        }

        // wait for data
        messageLengthReceived = false;
        bytesReceived = 0;

        WaitForData();
    }
    catch (SocketException socketException)
    {
        MessageBox.Show(socketException.Message);
    }
}

最佳答案

时间顺序应为:

  • 邮件长度的BeginReceive
  • EndReceive,用于完成#1
  • 邮件正文的
  • BeginReceive
  • EndReceive,用于完成#3

  • 例如。不使用回调,您可能会有:
    var sync = socket.BeginReceive(....);
    sync.AsyncWaitHandle.WaitOne();
    var res = socket.EndReceive(sync);
    sync = socket.BeginReceive(....);
    sync.AsyncWaitHandle.WaitOne();
    var res2 = socket.EndReceive(sync);
    
    但是,那么最好只使用Receive!
    我认为您可能会发现对两种不同的接收使用单独的处理程序会更容易:
    ... Start(....) {
        sync = socket.BeginReceive(.... MessageLengthReceived, null);
    }
    
    private void MessageLengthReceived(IAsyncResult sync) {
      var len = socket.EndReceive(sync);
      // ... set up buffer etc. for message receive
    
     sync = socket.BeginReceive(... MessageReceived, null);
    }
    
    private void MessageReceived(IAsyncResult sync) {
      var len = socket.EndReceive(sync);
      // ... process message
    }
    
    最终,将所有相关联放入状态对象并从BeginReceive传递(通过IAsyncResult.AsyncState通过完成委托(delegate)访问)可以使事情变得更容易,但确实偏离了命令式代码的线性思维和完全采用事件驱动的方法。

    2012附录:
    .NET 4.5版本
    借助C#5中的异步支持,有了一个新选项。这使用编译器从内联代码生成手动继续(单独的回调方法)和闭包(状态)。但是,有两件事需要解决:
  • 尽管System.Net.Sockets.Socket具有各种…Async方法,这些方法用于基于事件的异步模式,而不是C#5的Task使用的基于await的模式。解决方案:使用TaskFactory.FromAsyncTask<T> Begin…对中获取单个End…
  • TaskFactory.FromAsync仅支持将最多三个附加参数(除了回调和状态外)传递给Begin…。解决方案:带有零个附加参数的lambda具有正确的签名,而C#将为我们提供正确的闭包以将参数传入。

  • 因此,(更充分地实现了Message是另一种类型,它处理从初始发送以一定固定字节数编码的长度然后是内容字节到内容缓冲区长度的转换):
    private async Task<Message> ReceiveAMessage() {
      var prefix = new byte[Message.PrefixLength];
    
      var revcLen = await Task.Factory.FromAsync(
                             (cb, s) => clientSocket.BeginReceive(prefix, 0, prefix.Length, SocketFlags.None, cb, s),
                             ias => clientSocket.EndReceive(ias),
                             null);
      if (revcLen != prefix.Length) { throw new ApplicationException("Failed to receive prefix"); }
    
      int contentLength = Message.GetLengthFromPrefix(prefix);
      var content = new byte[contentLength];
    
      revcLen = await Task.Factory.FromAsync(
                             (cb, s) => clientSocket.BeginReceive(content, 0, content.Length, SocketFlags.None, cb, s),
                             ias => clientSocket.EndReceive(ias),
                             null);
      if (revcLen != content.Length) { throw new ApplicationException("Failed to receive content"); }
    
      return new Message(content);
    }
    

    关于C#Socket.BeginReceive/EndReceive,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1388561/

    10-11 08:51