C# Socket.BeginReceive/EndReceive [英] C# Socket.BeginReceive/EndReceive

查看:95
本文介绍了C# Socket.BeginReceive/EndReceive的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In what order is the Socket.BeginReceive/EndReceive functions called?

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

For instance, I call BeginReceive twice, once to get the message length and the second time to get the message itself. Now the scenario is like that, for every message I send, I start waiting for its completion (actually acknowledgment of the message sent, also I wait for the action's completion after receiving the acknowledgment), so I call BeginReceive with each BeginSend, but in each BeginReceive's callback, I check if I'm receiving the length or the message. If I'm receiving the message and have received it completely, then I call another BeginReceive to receive the completion of the action. Now this is where things get out of sync. Because one of my receive callback is receiving bytes which it interprets as the length of them message when in fact it is the message itself.

现在我该如何解决?

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

This is a C#.NET question :)

这是代码,基本上是太大了,抱歉

Here is the code, basically it is too big, sorry for that

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);
    }
}

推荐答案

时间顺序应该是:

  1. BeginReceive 消息长度
  2. EndReceive 用于完成#1
  3. BeginReceive 用于消息正文
  4. EndReceive 用于完成#3
  1. BeginReceive for message length
  2. EndReceive for the completion of #1
  3. BeginReceive for the message body
  4. EndReceive for the completion of #3

例如不使用您可能拥有的回调:

E.g. not using callbacks you could have:

var sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res = socket.EndReceive(sync);
sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res2 = socket.EndReceive(sync);

但是,您最好只使用 Receive

But then, you would be better just using Receive!

我认为您可能会发现为两个不同的接收使用单独的处理程序更容易:

I think you might find it easier to use separate handlers for the two different receives:

... 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 访问)可以使事情变得更容易,但确实改变了线性思维命令式代码和完全拥抱事件驱动的方法.

Ultimately putting all the associated in a state object and passing that around (in the completion delegate access via IAsyncResult.AsyncState) from BeginReceive can make things easier, but does take a shift from the linear thinking of imperative code and fulling embracing a event driven approach.

2012 年附录:

通过 C#5 中的异步支持,有一个新选项.这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态).但是,有两件事需要解决:

With the async support in C#5 there is a new option. This uses the compiler to generate the manual continuations (the separate callback methods) and closures (state) from inline code. However there are two things to work around:

  1. 虽然 System.Net.Sockets.Socket 有各种 ...Async 方法,但这些方法是针对基于事件的异步模式的,而不是 TaskC#5 的 await 使用的基于 code> 的模式.解决方案:使用 TaskFactory.FromAsyncBegin... End... 对中获取单个 Task.

  1. While System.Net.Sockets.Socket has various …Async methods these are for the event based asynchronous pattern, not the Task based pattern that C#5's await uses. Solution: use TaskFactory.FromAsync to get a single Task<T> from a Begin… End… pair.

TaskFactory.FromAsync 仅支持将最多三个附加参数(除了回调和状态之外)传递给 Begin....解决方案:采用零个附加参数的 lambda 具有正确的签名,C# 将为我们提供正确的闭包来传递参数.

TaskFactory.FromAsync only supports passing up to three additional arguments (in addition to the callback and state) to Begin…. Solution: a lambda taking zero additional arguments has the right signature, and C# will give us the right closure to pass the arguments in.

因此(更完全地实现,Message 是另一种类型,它处理从初始发送以固定数量的字节编码的长度然后内容字节到内容缓冲区长度的转换):

Hence (and more fully realised with Message being another type that handles the conversion from an initial send of the length encoded in some fixed number of bytes then the content bytes into a length for the content's buffer):

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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆