C#Socket.BeginReceive / EndReceive(C# Socket.Begin

2019-07-05 08:43发布

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

举例来说,我打电话BeginReceive两次,第一次得到的消息长度,第二时间得到消息本身。 现在的情况是这样的,每一个消息我送,我开始等待其完成(实际上是消息的确认发送,还我等动作的完成收到确认后),所以我打电话BeginReceive每个BeginSend,但在每个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);
    }
}

Answer 1:

在时间的顺序应该是:

  1. BeginReceive用于消息长度
  2. EndReceive为#1完成
  3. BeginReceive为邮件正文
  4. 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的异步支持,还有一个新的选择。 这使用编译器生成从内联代码手动延续(单独的回调方法)和封闭件(状态)。 但是有两件事情来解决:

  1. 虽然System.Net.Sockets.Socket有各种…Async方法这些都为基于事件的异步模式,而不是Task的基于模式的C#5的await用途。 解决方案:使用TaskFactory.FromAsync获得单个Task<T>Begin… End…对。

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


Answer 2:

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

伪代码:



// 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正确的例子



Answer 3:

通常的BeginXXX方法表示异步操作,而你似乎想要做一个同步的方式。

如果你确实需要一个同步客户端/服务器也许这将帮助http://sharpoverride.blogspot.com/2009/04/another-tcpip-server-client-well-it.html



Answer 4:

它将帮助,如果你描述你要发送的消息的结构。

只要你只有一个BeginReceive()突出,它将完成,给你上线数据的下一个可用的字节。 如果你有一个以上的在同一时间出色,那么所有的赌注都关闭,因为.NET不保证建成,将在任何给定的顺序。



Answer 5:

正如其他人所说的,这里不使用全局变量 - 使用类套接字状态。 就像是:

public class StateObject
{
    public const int DEFAULT_SIZE = 1024;           //size of receive buffer

    public byte[] buffer = new byte[DEFAULT_SIZE];  //receive buffer
    public int dataSize = 0;                        //data size to be received
    public bool dataSizeReceived = false;           //received data size?
    public StringBuilder sb = new StringBuilder();  //received data String
    public int dataRecieved = 0;

    public Socket workSocket = null;                //client socket.
    public DateTime TimeStamp;                      //timestamp of data
} //end class StateObject

试图重新发送邮件之前,您应该验证插座......你可能有一个插座例外。

你或许应该有回报; 后ReceiveComplete的“如果”块你WaitForData电话。

蒂莫西普拉特利表示它上面,一个错误将在bytesRecieved第二次通过。 每次只从EndReceive测量,并与BytesReceived然后比较它messageLength。 你需要保持所有bytesRecieved的总和。

和你最大的错误是,在您第一次调用ReceiveComplete,你考虑一个事实,即该消息可能(最有可能)含有不是消息的只是大小更多的数据 - 它可能会包含一半的消息也是如此。 您需要剥离的数据大小,然后也是消息的其余部分存储在您的消息变量。



文章来源: C# Socket.BeginReceive/EndReceive