11

Socket.BeginReceive / EndReceive関数はどのような順序で呼び出されますか?

たとえば、BeginReceiveを2回呼び出します。1回目はメッセージの長さを取得し、2回目はメッセージ自体を取得します。これで、シナリオは次のようになります。送信するすべてのメッセージについて、完了を待ち始めます(実際には、送信されたメッセージの確認応答です。また、確認応答を受信した後、アクションの完了を待ちます)。したがって、BeginSendごとBeginReceiveを呼び出します。 BeginReceiveのコールバックごとに、長さまたはメッセージを受信して​​いるかどうかを確認します。メッセージを受信して​​完全に受信した場合は、別のBeginReceiveを呼び出しますアクションの完了を受け取ります。今、これは物事が同期しなくなるところです。私の受信コールバックの1つは、実際にはメッセージ自体であるにもかかわらず、メッセージの長さとして解釈されるバイトを受信して​​いるためです。

どうすれば解決できますか?

編集:これは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() が 1 つしかない限り、処理は完了し、ネットワーク上で次の使用可能なデータのバイトが提供されます。同時に複数の未払いがある場合、すべての賭けは無効になります。これは、.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 が言ったように、1 つのエラーは 2 回目に受信したバイト数になります。そのEndReceiveからbytesReceivedのみを測定し、それをmessageLengthと比較するたびに。すべての bytesRecieved の合計を保持する必要があります。

そして、あなたの最大のエラーは、ReceiveComplete への最初の呼び出しで、メッセージに (おそらく) メッセージのサイズよりも多くのデータが含まれる可能性があるという事実を考慮に入れることです。おそらく、メッセージの半分も含まれます。データサイズを剥がしてから、残りのメッセージもメッセージ変数に保存する必要があります。

于 2010-10-07T02:03:21.603 に答える