当前位置:  开发笔记 > 编程语言 > 正文

C#Socket.BeginReceive/EndReceive

如何解决《C#Socket.BeginReceive/EndReceive》经验,为你挑选了2个好方法。

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

例如,我调用BeginReceive两次,一次获取消息长度,第二次调用消息本身.现在的情况是这样的,对于我发送的每条消息,我开始等待它的完成(实际上确认发送的消息,我等待接收到确认后的动作完成),所以我用每个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);
    }
}

Richard.. 20

时间顺序应该是:

    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
}

最终将所有关联放在一个状态对象中并IAsyncResult.AsyncState从BeginReceive 传递它(在完成委托访问中)可以使事情变得更容易,但确实从命令式代码的线性思维转变并完全采用事件驱动的方法.


2012年附录:

.NET 4.5版本

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

    虽然System.Net.Sockets.Socket有各种…Async方法,但这些方法适用于基于事件的异步模式,而不是TaskC#5 await使用的基础模式.解决方案:用于从一对中TaskFactory.FromAsync获取单个.TaskBegin… End…

    TaskFactory.FromAsync 只支持传递最多三个额外的参数(除了回调和状态)Begin….解决方案:一个带有零个额外参数的lambda具有正确的签名,而C#将为我们提供正确的闭包来传递参数.

因此(并且更充分地实现了Message另一种类型,它处理从以某个固定数量的字节编码的长度的初始发送转换然后将内容字节转换为内容缓冲区的长度):

private async Task 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);
}


Timothy Prat.. 6

也许你想要做的就是连接你的回调:

伪代码:


// read the first 2 bytes as message length
BeginReceive(msg,0,2,-,-,new AsyncCallback(LengthReceived),-)

LengthReceived(ar) {
  StateObject so = (StateObject) ar.AsyncState;
  Socket s = so.workSocket;
  int read = s.EndReceive(ar);
  msg_length = GetLengthFromBytes(so.buffer);
  BeginReceive(so.buffer,0,msg_length,-,-,new AsyncCallback(DataReceived),-)
}

DataReceived(ar) {
  StateObject so = (StateObject) ar.AsyncState;
  Socket s = so.workSocket;
  int read = s.EndReceive(ar);
  ProcessMessage(so.buffer);
  BeginReceive(so.buffer,0,2,-,-,new AsyncCallback(LengthReceived),-)
}

请参阅:http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx以获取正确的示例



1> Richard..:

时间顺序应该是:

    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
}

最终将所有关联放在一个状态对象中并IAsyncResult.AsyncState从BeginReceive 传递它(在完成委托访问中)可以使事情变得更容易,但确实从命令式代码的线性思维转变并完全采用事件驱动的方法.


2012年附录:

.NET 4.5版本

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

    虽然System.Net.Sockets.Socket有各种…Async方法,但这些方法适用于基于事件的异步模式,而不是TaskC#5 await使用的基础模式.解决方案:用于从一对中TaskFactory.FromAsync获取单个.TaskBegin… End…

    TaskFactory.FromAsync 只支持传递最多三个额外的参数(除了回调和状态)Begin….解决方案:一个带有零个额外参数的lambda具有正确的签名,而C#将为我们提供正确的闭包来传递参数.

因此(并且更充分地实现了Message另一种类型,它处理从以某个固定数量的字节编码的长度的初始发送转换然后将内容字节转换为内容缓冲区的长度):

private async Task 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);
}



2> Timothy Prat..:

也许你想要做的就是连接你的回调:

伪代码:


// read the first 2 bytes as message length
BeginReceive(msg,0,2,-,-,new AsyncCallback(LengthReceived),-)

LengthReceived(ar) {
  StateObject so = (StateObject) ar.AsyncState;
  Socket s = so.workSocket;
  int read = s.EndReceive(ar);
  msg_length = GetLengthFromBytes(so.buffer);
  BeginReceive(so.buffer,0,msg_length,-,-,new AsyncCallback(DataReceived),-)
}

DataReceived(ar) {
  StateObject so = (StateObject) ar.AsyncState;
  Socket s = so.workSocket;
  int read = s.EndReceive(ar);
  ProcessMessage(so.buffer);
  BeginReceive(so.buffer,0,2,-,-,new AsyncCallback(LengthReceived),-)
}

请参阅:http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx以获取正确的示例

推荐阅读
手机用户2402851335
这个屌丝很懒,什么也没留下!