Что останавливает поток данных с помощью асинхронной системы .NET 3.5 System.Net.Sockets.Socket?

У меня есть интерфейс сокета клиент/сервер .NET 3.5, использующий асинхронные методы. Клиент подключается к серверу, и соединение должно оставаться открытым до завершения работы приложения. Протокол состоит из следующего шаблона:

  1. отправить stx
  2. получить подтверждение
  3. отправить данные1
  4. получить подтверждение
  5. отправить data2 (повторить 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);
}

Трассировка .NET показывает следующее при отправке ABC\r\n:

Socket#7588182::SendAsync()
Socket#7588182::SendAsync(True#1)
Data from Socket#7588182::FinishOperation(SendAsync)
00000000 : 41 42 43 0D 0A
Socket#7588182::ReceiveAsync()
Exiting Socket#7588182::ReceiveAsync()         -> True#1

И это останавливается там. Это похоже на первую отправку от клиента, но сервер не проявляет активности.

Я думаю, что на данный момент это может быть переизбыток информации, но я буду рад предоставить более подробную информацию по мере необходимости.

Спасибо!


person TonyG    schedule 26.05.2010    source источник


Ответы (1)


Во-первых, я уверен, что вы знаете, но это стоит повторить; TCP-соединение представляет собой поток байтов. Каждое чтение может возвращать от 1 байта до размера буфера, который вы использовали, в зависимости от объема полученных данных. Тот факт, что вы отправляете данные с помощью 3 отправок, не означает, что вам понадобятся 3 recv для их чтения, вы можете получить все это в одном, или каждый recv может возвращать один байт. Это зависит от вас, чтобы иметь дело с фреймингом сообщений, поскольку вы единственный, кто знает об этом. TCP — это поток байтов.

Также ПОЧЕМУ вы используете эти события? Если вы не хотите использовать асинхронный поток данных, не используйте асинхронные функции, напишите что-нибудь очень простое с функциями синхронного сокета и уберите всю бессмысленную сложность использования асинхронного API, а затем используйте примитивы синхронизации, чтобы затормозить его.

person Len Holgate    schedule 26.05.2010