11

调用 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);
    }
}
4

5 回答 5

22
于 2009-09-07T10:38:15.217 回答
6

Perhaps what you want to do is chain your call-backs :

pseudo code:



// 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),-)
}

see: http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx for correct examples

于 2009-09-07T10:39:59.650 回答
1

如果您描述要发送的消息的结构,将会有所帮助。

只要您只有一个未完成的 BeginReceive(),它就会完成并为您提供在线路上的下一个可用数据字节。如果您同时有多个未完成项目,则所有投注均无效,因为 .net 不保证按任何给定顺序完成。

于 2009-09-14T23:15:32.340 回答
1

通常 BeginXXX 方法表示异步操作,而您似乎希望以同步方式进行。

如果您确实想要一个同步的客户端/服务器,这可能会有所帮助http://sharpoverride.blogspot.com/2009/04/another-tcpip-server-client-well-it.html

于 2009-09-07T10:29:44.003 回答
0

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

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 的“if”块中调用 WaitForData 之后。

蒂莫西·普拉特利(Timothy Pratley)在上面说过,一个错误将在第二次接收到的字节数中出现。每次您只测量来自该 EndReceive 的 bytesReceived,然后将其与 messageLength 进行比较。您需要保留所有字节数的总和。

您最大的错误是,在您第一次调用 ReceiveComplete 时,您考虑到消息可能(很可能)包含比消息大小更多的数据 - 它也可能包含一半的消息。您需要剥离数据大小,然后还将消息的其余部分存储在您的消息变量中。

于 2010-10-07T02:03:21.603 回答