C#Socket.BeginReceive / EndReceiveSocket、BeginReceive、EndReceive

2023-09-02 20:49:25 作者:安稳又干净

在什么样的顺序是Socket.BeginReceive / EndReceive功能叫什么名字?

例如,我称之为 BeginReceive 两次,第一次得到的消息长度和第二次得到的消息本身。现在的情况是这样的,因为每封邮件我送,我开始等待其完成(实际上该消息的确认发送,还我等动作的完成收到确认后),所以我称之为 BeginReceive 每个 BeginSend ,但在每一个 BeginReceive 的回调,我检查,如果我收到的长度或消息。如果我收到的消息,已经完全接受它,然后我又打电话 BeginReceive 接收完成的动作。现在,这是事情变得不同步。因为我收到的回调之一是接收字节其中除$ P $点是他们的消息的长度,而实际上它是消息本身。

现在我该如何解决呢?

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

Socket通信BeginReceive异步接收数据何时回调Callback

下面是code,基本上它是太大了,很抱歉。

 公共无效发送(字符串消息)
{
    尝试
    {
        bytesSent = 0;

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

        clientSocket.BeginSend(writeDataBuffer,bytesSent,messageSendSize,SocketFlags.None,
                            新的AsyncCallback(SendComplete),ClientSocket的);
    }
    赶上(SocketException socketException)
    {
        的MessageBox.show(socketException.Message);
    }
}

公共无效WaitForData()
{
    尝试
    {
        如果(!messageLengthReceived)
        {
            clientSocket.BeginReceive(receiveDataBuffer,与BytesReceived,MESSAGE_LENGTH_SIZE  - 与BytesReceived,
                                    SocketFlags.None,新的AsyncCallback(RecieveComplete),ClientSocket的);
        }
}

公共无效发送(字符串消息)
{
    尝试
    {
        bytesSent = 0;

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

        clientSocket.BeginSend(writeDataBuffer,bytesSent,messageSendSize,SocketFlags.None,
                            新的AsyncCallback(SendComplete),ClientSocket的);
    }
    赶上(SocketException socketException)
    {
        的MessageBox.show(socketException.Message);
    }
}

公共无效WaitForData()
{
    尝试
    {
        如果(!messageLengthReceived)
        {
            clientSocket.BeginReceive(receiveDataBuffer,与BytesReceived,MESSAGE_LENGTH_SIZE  - 与BytesReceived,
                                    SocketFlags.None,新的AsyncCallback(RecieveComplete),ClientSocket的);
        }
        其他
        {
            clientSocket.BeginReceive(receiveDataBuffer,与BytesReceived,messageLength  - 与BytesReceived,
                                    SocketFlags.None,新的AsyncCallback(RecieveComplete),ClientSocket的);
        }
    }
    赶上(SocketException socketException)
    {
        的MessageBox.show(socketException.Message);
    }
}

公共无效RecieveComplete(IAsyncResult的结果)
{
    尝试
    {
        Socket套接字= result.AsyncState的插座;
        =与BytesReceived socket.EndReceive(结果);

        如果(!messageLengthReceived)
        {
            如果(与BytesReceived!= MESSAGE_LENGTH_SIZE)
            {
                WaitForData();
                返回;
            }

            //展开消息长度
            INT长度= BitConverter.ToInt32(receiveDataBuffer,0);
            长度= IPAddress.NetworkToHostOrder(长度);

            messageLength =长度;
            messageLengthReceived = TRUE;

            与BytesReceived = 0;

            //现在等待获取消息本身
            WaitForData();
        }
        其他
        {
            如果(与BytesReceived!= messageLength)
            {
                WaitForData();
            }
            其他
            {
                字符串消息= Encoding.ASCII.GetString(receiveDataBuffer);

                的MessageBox.show(消息);

                与BytesReceived = 0;
                messageLengthReceived = FALSE;

                //清除缓冲区
                receiveDataBuffer =新的字节[AsyncClient.BUFFER_SIZE]

                WaitForData();
            }
        }
    }
    赶上(SocketException socketException)
    {
        的MessageBox.show(socketException.Message);
    }

}

公共无效SendComplete(IAsyncResult的结果)
{
    尝试
    {
        Socket套接字= result.AsyncState的插座;
        bytesSent = socket.EndSend(结果);

        如果(bytesSent!= messageSendSize)
        {
            messageSendSize  -  = bytesSent;

            socket.BeginSend(writeDataBuffer,bytesSent,messageSendSize,SocketFlags.None,
                            新的AsyncCallback(SendComplete),ClientSocket的);
            返回;
        }

        //等待数据
        messageLengthReceived = FALSE;
        与BytesReceived = 0;

        WaitForData();
    }
    赶上(SocketException socketException)
    {
        的MessageBox.show(socketException.Message);
    }
}
 

解决方案

在时间顺序应该是:

BeginReceive 消息长度 EndReceive 为完成#1 BeginReceive 的消息体 EndReceive 为完成#3

例如。不使用回调,你可以有:

  VAR同步= socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
VAR解析度= socket.EndReceive(同步);
同步= socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
VAR RES2 = socket.EndReceive(同步);
 

不过,你只用更好接收

我想你可能会发现它更容易使用单独的处理程序两种不同的接收:

  ...开始(....){
    同步= socket.BeginReceive(.... MessageLengthReceived,NULL);
}

私人无效MessageLengthReceived(IAsyncResult的同步){
  VAR的len = socket.EndReceive(同步);
  // ...设置缓冲区等消息接收

 同步= socket.BeginReceive(...的messageReceived,NULL);
}

私人无效的messageReceived(IAsyncResult的同步){
  VAR的len = socket.EndReceive(同步);
  // ...处理消息
}
 

最后把所有相关的状态对象(通过 IAsyncResult.AsyncState 在完成代理访问)传递,大约从BeginReceive可以使事情变得更容易,但确实需要从势在必行code中的线性思维的转变和缩绒拥抱事件驱动的方法。

2012附录的:

.NET 4.5版本

在C#5异步支持,有一个新的选择。它使用编译器生成内联code手动延续(单独的回调方法)和封盖(状态)。但是有两件事情来解决:

System.Net.Sockets.Socket 有不同的 ...异步方法,这些都为事件基于异步模式,而不是工作基于模式的C#5的计谋使用。解决方法:使用 TaskFactory.FromAsync 来获得一个任务< T> 开始... 完... 对。

TaskFactory.FromAsync 仅支持通过最多三个额外的参数(除了回调状态),以开始... 。解决方案:一个lambda采取额外的零的参数具有正确的签名,和C#会给我们正确的封闭传递参数的

因此​​,(和更充分地实现了与消息是另一种类型的处理转换,从最初的长度发送连接codeD在一些固定的字节数,然后内容字节到一个长度为内容的缓冲区):

 专用异步任务<消息> ReceiveAMessage(){
  VAR preFIX =新的字节[信息prefixLength。]。

  VAR revcLen =等待Task.Factory.FromAsync(
                         (CB,S)=> clientSocket.BeginReceive(preFIX,0,prefix.Length,SocketFlags.None,CB,多个),
                         IAS => clientSocket.EndReceive(IAS),
                         空值);
  如果(!revcLen = prefix.Length){抛出新ApplicationException的(无法接受preFIX); }

  INT CONTENTLENGTH = Message.GetLengthFrom preFIX(preFIX);
  VAR内容=新的字节[CONTENTLENGTH]

  revcLen =等待Task.Factory.FromAsync(
                         (CB,S)=> clientSocket.BeginReceive(含量,0,content.Length,SocketFlags.None,CB,多个),
                         IAS => clientSocket.EndReceive(IAS),
                         空值);
  如果(!revcLen = content.Length){抛出新ApplicationException的(无法接收内容); }

  返回新的信息(内容);
}
 

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

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.

Now how do I resolve it?

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

解决方案

The order in time should be:

BeginReceive for message length EndReceive for the completion of #1 BeginReceive for the message body 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);

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
}

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 Addendum:

.NET 4.5 Version

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:

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 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.

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