2013-12-11 3 views
1

Я пишу базовый сервер TCP/IP для приема сообщений от существующего клиента. В настоящее время я могу получать сообщения с одним и несколькими буферами. Проблема возникает, когда клиент отправляет множество сообщений. Вместо того, чтобы отправлять каждое сообщение в виде единого сообщения, он ожидает, что поток будет открыт, и все сообщения в наборе будут отправляться друг за другом до того, как поток снова закроется. Я закодированы для этого в отредактированной фрагменте ниже:NetworkStream.DataAvailable slow to update

private void AcceptMessage(IAsyncResult ar) 
{ 
    String receivedMessage = ""; 

    // Only run if the server is listening. Otherwise, an exception will be thrown. 
    if (isListening) 
    { 
     ASCIIEncoding encoder = new ASCIIEncoding(); 
     try 
     { 
      TcpListener listener = (TcpListener)ar.AsyncState; 
      using (TcpClient client = listener.EndAcceptTcpClient(ar)) 
      using (NetworkStream stream = client.GetStream()) 
      { 
       int bytesRead; 
       int totalBytes = 0; 
       stream.ReadTimeout = ReadTimeout; 
       do 
       { 
        byte[] receivedBytes = new byte[BufferSize]; 
        StringBuilder message = new StringBuilder(); 
        bytesRead = 0; 
        do 
        { 
         try 
         { 
          if (stream.CanRead) 
          bytesRead = stream.Read(receivedBytes, 0, BufferSize); 
         } 
         catch (...Exception Handling...) 

         if (bytesRead == 0) 
          break; 

         message.Append(encoder.GetString(receivedBytes, 0, bytesRead)); 
         totalBytes += bytesRead; 
        } while (bytesRead == BufferSize);  // Allow for multiple buffers in a single message 
        byte[] ack = encoder.GetBytes(GetAck(message.ToString(), ackNak, status, statusMessage)); 
        stream.Write(ack, 0, ack.Length); 
        ...Message Processing... 
       } while (stream.DataAvailable);   // Allow for multiple messages       
      } 
     } 
     catch (...Error Handling...) 
     finally { ...Cleanup...} 
    } 
} 

Проблема возникает на втором while (stream.DataAvailable); Когда происходит проверка, DataAvailable показывает ложные при передаче набора сообщений, но если я ставлю перерыв на следующей строке , это покажет правду. Другими словами, он проверяет, доступны ли данные до его доступности. Я мог бы исправить это, добавив задержку в одну секунду, прежде чем проверять, есть ли больше сообщений, но в 99 из 100 случаев это было бы ненужной задержкой. Поскольку клиент может отправлять только одно сообщение (или набор сообщений) за раз, он может добавить значительное количество времени на прием сообщений во время занятости.

Есть ли лучший способ определить, есть ли еще ожидающие сообщения (без модификаций клиента)? Благодаря!

EDIT

Я изменил код, чтобы включить предложения. Я добавил тайм-аут и изменил первую проверку с stream.DataAvailable на bytesRead == BufferSize. У меня нет доступа к ПО клиента, поэтому я не могу добавить передачу МНВ. Я также не гарантирую, будет ли отправлено несколько сообщений в одном потоке или нескольких отдельных потоках. Я до сих пор не понял, как узнать, есть ли еще ожидающие сообщения. Я не могу оставить поток открытым, потому что это блокирует следующую передачу.

Я отметил Элгонзо как ответ, потому что он помог с большинством аспектов моего вопроса. Он по-прежнему не ответил, как определить, ожидаются ли сообщения, что может быть невозможно, но мой менеджер решил, что, как только соединение будет открыто, мы должны оставить его открытым как выделенное соединение. Это работает, поскольку наши слушатели общаются только с одним устройством по внутренней защищенной сети с постоянным трафиком. Мы добавляем тайм-аут, чтобы отключиться, если на какое-то время неактивно.

+0

Существует мало правильных вариантов использования доступных, и это не один из них. Вы должны либо блокировать, либо читать, пока не получите то, что вам нужно, или используйте async или non-blocking I/O ditto. – EJP

ответ

2

Доступны ли данные или нет, а скорость (и периодичность) данных, поступающих со стороны сервера, зависит не только от времени/поведения клиента, но также от факторов скорости и латентности сети/интернет-соединения между ними (что само по себе зависит от многих и неизвестных факторов).

В результате вы не можете использовать stream.DataAvailable в своем внешнем while-loop, чтобы проверить, появятся или нет дополнительные данные. Вероятно, вам нужно будет установить ReadTimeout в NetworkStream, чтобы узнать, когда клиент сделан/отключен.

Также предпочтительно реализовать конкретный сигнал/сообщение, которое будет отправлено клиентом, когда клиент собирается завершить его соединение, и не только полагаться на таймаут. Это поможет вам устранить проблемы, когда неясно, является ли причиной ошибка на сервере или клиенте или возникают проблемы из-за проблем с сетевыми подключениями.

Детали реализации в ответ на этот вопрос (хотя другой сценарий применения подхода является то же самое): Reading from closed NetworkStream doesn't cause any exception

Кроме того, даже ваш внутренний пока контур может не на дрянных/сетевые соединения медленно, и может оставить вас с частично полученным сообщением от клиента, только потому, что stream.DataAvailable может быть ложным на мгновение до того, как будет получен следующий пакет данных (продолжение текущего сообщения) ...

+0

Благодарим вас за предложения. К сожалению, у меня нет доступа к коду Client, поэтому я не могу добавить маркер EndMessage. Тем не менее, я установил тайм-аут чтения и проверит, заполнен ли буфер, чтобы получить представление о том, было ли отправлено все сообщение или еще больше пришло. К сожалению, у меня все еще нет надежного способа определить, будет ли передано второе сообщение в том же потоке. Спасибо вам за ваши предложения. – Tim

+0

К сожалению, вы не можете точно сказать, будет ли больше отправлено клиентом дата, если информация, отправленная клиентом, уже не содержит такую ​​информацию. Я забыл упомянуть в своем ответе, что вы также не можете полагаться исключительно на * Read() * return '0' (соединение a-ka закрыто), поскольку любой дрянной прокси-сервер или фанковая штука между вашим серверным сокетом и вашим клиентским сокетом может держите серверное соединение открытым в течение длительного времени, хотя клиент давно мертв ... – elgonzo

0

Я предлагаю начать собственный поток, который работает в фоновом режиме и просто считывает данные из потока, если имеются данные. Если данные получены, просто собирайте данные до тех пор, пока не получите полное сообщение (которое должно указываться определенной байтовой или байтовой последовательностью), а затем выпустите событие. По-моему, это должно помочь избежать проблемы.

 Смежные вопросы

  • Нет связанных вопросов^_^