2

我有一个使用异步方法的 .NET 3.5 客户端/服务器套接字接口。客户端连接到服务器,并且连接应保持打开状态,直到应用程序终止。该协议由以下模式组成:

  1. 发送 stx
  2. 接收确认
  3. 发送数据1
  4. 接收确认
  5. 发送数据2(重复5-6,同时有更多数据)
  6. 接收确认
  7. 发送 etx

因此,具有上述两个数据块的单个事务将包含来自客户端的 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
}

服务器上的代码非常相似,只是它先接收然后发送响应 - 服务器在发送响应后没有做任何事情(我可以​​告诉)来修改连接。问题是我第二次在客户端点击 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);
}

发送 ABC 时,.NET 跟踪显示以下内容\r\n:

套接字#7588182::SendAsync()
套接字#7588182::SendAsync(True#1)
来自 Socket#7588182::FinishOperation(SendAsync) 的数据
00000000 : 41 42 43 0D 0A
套接字#7588182::ReceiveAsync()
退出 Socket#7588182::ReceiveAsync() -> True#1

它停在那里。它看起来就像来自客户端的第一次发送,但服务器没有显示任何活动。

我认为现在可能是信息过载,但我很乐意根据需要提供更多详细信息。

谢谢!

4

1 回答 1

1

首先,我相信你知道,但值得重复一遍;TCP 连接是一个字节流。每次读取可以返回 1 个字节和您使用的缓冲区大小,具体取决于已到达的数据量。仅仅因为您使用 3 次发送发送数据并不意味着您需要 3 个 recv 来读取它,您可以一次获得所有数据,或者每个 recv 可以返回一个字节。处理消息框架取决于您,因为您是唯一知道它的人。TCP 是字节流。

另外,您为什么要使用这些事件?如果您不想使用异步数据流,请不要使用异步函数,使用同步套接字函数编写一些非常简单的东西,并消除使用异步 API 的所有无意义的复杂性,然后使用同步原语来阻碍它。

于 2010-05-26T06:26:20.127 回答