2

非同期メソッドを使用する.NET3.5クライアント/サーバーソケットインターフェイスがあります。クライアントはサーバーに接続し、アプリが終了するまで接続は開いたままにする必要があります。プロトコルは次のパターンで構成されています。

  1. stxを送信します
  2. ackを受け取る
  3. データを送信1
  4. ackを受け取る
  5. data2を送信します(さらにデータを追加しながら5〜6を繰り返します)
  6. ackを受け取る
  7. etxを送信

したがって、上記のように2つのデータブロックを持つ単一のトランザクションは、クライアントからの4つの送信で構成されます。etxを送信した後、クライアントはさらにデータが送信されるのを待ってから、stxで次の送信を開始します。個々の交換間または各stx/data/etxペイロードの後で接続を切断したくありません。

現在、接続後、クライアントは最初のstxを送信し、単一のackを取得できますが、それ以降はデータをネットワークに追加できません。どちらの側も切断せず、ソケットはそのままです。クライアントコードは次のように真剣に省略されています-私はオンラインコードサンプルで一般的に利用可能なパターンに従っています。

private void SendReceive(string data) {
    // ...
    SocketAsyncEventArgs completeArgs;
    completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend);
    clientSocket.SendAsync(completeArgs);
    // two AutoResetEvents, one for send, one for receive
    if ( !AutoResetEvent.WaitAll(autoSendReceiveEvents , -1) )
       Log("failed");
    else
       Log("success");
    // ...
}

private void OnSend( object sender , SocketAsyncEventArgs e ) {
// ...
    Socket s = e.UserToken as Socket;
    byte[] receiveBuffer = new byte[ 4096 ];
    e.SetBuffer(receiveBuffer , 0 , receiveBuffer.Length);
    e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
    s.ReceiveAsync(e);
// ...
}

private void OnReceive( object sender , SocketAsyncEventArgs e ) {}
    // ...
    if ( e.BytesTransferred > 0 ) {
        Int32 bytesTransferred = e.BytesTransferred;
        String received = Encoding.ASCII.GetString(e.Buffer , e.Offset , bytesTransferred);
        dataReceived += received;
    }
    autoSendReceiveEvents[ SendOperation ].Set(); // could be moved elsewhere
    autoSendReceiveEvents[ ReceiveOperation ].Set(); // releases mutexes
}

サーバー上のコードは、最初に受信してから応答を送信することを除いて、非常に似ています。サーバーは、応答を送信した後、接続を変更するために何もしていません(私が知ることができます)。問題は、クライアントで2回目にSendReceiveを押すと、接続がすでに奇妙な状態になっていることです。

SocketAsyncEventArgsを保持し、ソケット/接続の存続期間中同じオブジェクトを再利用するために、クライアントで何かを行う必要がありますか?接続または特定の交換の存続期間中に、どのeventargsオブジェクトを使用する必要があるかわかりません。

サーバーがデータを受信し続けることを保証するために、サーバーで何かをする必要がありますか、それとも何もしませんか?

サーバーのセットアップと応答の処理は次のようになります。

void Start() {
    // ...
    listenSocket.Bind(...);
    listenSocket.Listen(0);
    StartAccept(null); // note accept as soon as we start. OK?
    mutex.WaitOne();
}

void StartAccept(SocketAsyncEventArgs acceptEventArg) {
    if ( acceptEventArg == null )
    {
        acceptEventArg = new SocketAsyncEventArgs();
        acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
    }
    Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);
    if ( !willRaiseEvent )
        ProcessAccept(acceptEventArg);
    // ...
}

private void OnAcceptCompleted( object sender , SocketAsyncEventArgs e ) {
    ProcessAccept(e);
}

private void ProcessAccept( SocketAsyncEventArgs e ) {
    // ...
    SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
    readEventArgs.SetBuffer(dataBuffer , 0 , Int16.MaxValue);
    readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
    readEventArgs.UserToken = e.AcceptSocket;
    dataReceived = ""; // note server is degraded for single client/thread use
    // As soon as the client is connected, post a receive to the connection.
    Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
    if ( !willRaiseEvent )
        this.ProcessReceive(readEventArgs);
    // Accept the next connection request.
    this.StartAccept(e);
}

private void OnIOCompleted( object sender , SocketAsyncEventArgs e ) {
    // switch ( e.LastOperation )
    case SocketAsyncOperation.Receive:
        ProcessReceive(e); // similar to client code
        // operate on dataReceived here
    case SocketAsyncOperation.Send:
        ProcessSend(e); // similar to client code
}

// execute this when a data has been processed into a response (ack, etc)
private SendResponseToClient(string response) {
    // create buffer with response
    // currentEventArgs has class scope and is re-used
    currentEventArgs.SetBuffer(sendBuffer , 0 , sendBuffer.Length);
    Boolean willRaiseEvent = currentClient.SendAsync(currentEventArgs);
    if ( !willRaiseEvent )
        ProcessSend(currentEventArgs);
}

.NETトレースは、ABCを送信するときに次のことを示しています\ r \n:

Socket#7588182 :: SendAsync()
Socket#7588182 :: SendAsync(True#1)
Socket#7588182 :: FinishOperation(SendAsync)からのデータ
00000000:41 42 43 0D 0A
Socket#7588182 :: ReceiveAsync()
Socket#7588182 :: ReceiveAsync()-> True#1を終了します

そしてそれはそこで止まります。クライアントからの最初の送信と同じように見えますが、サーバーはアクティビティを表示しません。

今のところ情報過多の可能性があると思いますが、必要に応じて詳細をお知らせします。

ありがとう!

4

1 に答える 1

1

まず、あなたが知っていると確信していますが、繰り返す価値があります。TCP接続はバイトのストリームです。各読み取りは、到着したデータの量に応じて、1バイトから使用したバッファーのサイズまでを返すことができます。3回の送信でデータを送信するからといって、それを読み取るために3回の受信が必要になるわけではありません。すべてを1回で取得することも、各受信が1バイトを返すこともできます。メッセージフレーミングについて知っているのはあなただけなので、メッセージフレーミングに対処するのはあなた次第です。TCPはバイトのストリームです。

また、なぜそれらのイベントを使用しているのですか?非同期データフローを使用したくない場合は、非同期関数を使用せず、同期ソケット関数を使用して非常に単純なものを記述し、非同期APIを使用してから、同期プリミティブを使用してそれを妨害するという無意味な複雑さをすべて取り除きます。

于 2010-05-26T06:26:20.127 に答える