EndReceive никогда не возвращает 0, как узнать, когда пакеты заканчиваются в tcp / ip? - PullRequest
1 голос
/ 03 января 2011

Это похоже на мой последний вопрос. Я делаю простую программу tcp / ip chat, и у меня возникли небольшие трудности с функцией обратного вызова EndReceive. Я вставил в реализацию Microsoft (см. Ниже), и я заметил, что если сообщение было прочитано полностью, оно не будет запускать функцию обратного вызова до тех пор, пока не будет отправлено следующее сообщение, т.е. EndReceive никогда не вернет 0. Например если я отправлю текстовое сообщение из 5 символов и размер буфера будет равен 20, значение read будет равно 5, я обработаю строку и вызову BeginReceive, однако BeginReceive не будет работать, пока сервер не отправит другое сообщение. Раздел else никогда не достигается, потому что s.EndReceive никогда не возвращает 0. Как узнать, когда данные были получены полностью?

public static void Read_Callback(IAsyncResult ar){
 StateObject so = (StateObject) ar.AsyncState;
 Socket s = so.workSocket;

 int read = s.EndReceive(ar);

 if (read > 0) {
            so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
            s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, 
                                  new AsyncCallback(Async_Send_Receive.Read_Callback), so);
 }
 else{
      if (so.sb.Length > 1) {
           //All of the data has been read, so displays it to the console
           string strContent;
           strContent = so.sb.ToString();
           Console.WriteLine(String.Format("Read {0} byte from socket" + 
                           "data = {1} ", strContent.Length, strContent));
      }
      s.Close();
 }
}

1 Ответ

3 голосов
/ 03 января 2011

EndReceive вполне может получить 0, если поток закрыт и все данные были использованы.Однако для большинства других целей в потоке нет логического разрыва - и даже NetworkStream.DataAvailable говорит только о том, что доступно на клиенте , а не о том, что было отправлено сервером .Следовательно: если вы отправляете несколько сообщений / etc в одном и том же потоке, обычно необходимо или ставить перед каждым блоком число ожидаемых байтов, или , чтобы использовать некоторую последовательность разделителей, которая быобычно не ожидается в середине контента.Первое часто проще, если содержимое может быть произвольным двоичным содержимым (т. Е. Сложно предсказать последовательность, которая не ожидается), но второе позволяет выполнять потоковую передачу без , заранее зная длины (полезно, еслиВы вычисляете контент на лету, так как в противном случае вам может понадобиться буферизовать контент сначала , чтобы узнать длину (или выполнить пробный прогон без данных).

Также обратите вниманиечто Read не гарантирует возврата всех доступных данных - было бы вполне законно отправить "HELLO", а Read вернуло бы только 1 (H). Следовательно, вы должны ожидать объединения нескольких Read вызовы в одно сообщение (опять же, с использованием префикса длины или ожидаемой последовательности разделителя).

В этом сценарии у меня будет соблазн отправить HELLO\0 (где \0 - ноль-byte). Тогда я бы потреблял через Read, пока или не вернет <=0, или a \0. В случае \0 все, что буферизовано дотерминатор репрпосылает одно сообщение;что-нибудь после разделитель должен быть сохранен и обработан как часть следующего сообщения или сообщений.В качестве тестового примера представьте, что вы получите Read return 13 с последовательностью a\0b\0cde\0f\0\0\0g, которая должна "a", "b", "cde", "f", "", "", "g"(отмечая, что g не должен обрабатываться - он может быть частью более длинных последовательностей, которые еще не получены - буферизировать до получения \0).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...