2013-04-03 2 views
3

Я пытаюсь написать асинхронный сокет в C#. Я прочитал много статей msdn и нашел эти два примера: server, client.Как определить конец потока на сервере асинхронного сокета

Я понял пример кода и использовал его для реализации своего собственного асинхронного сокета. В этом примере сервер проверяет <EOF>, чтобы определить конец потока и отправить ответ. Я хотел бы знать, не проверяя специальный литерал, когда поток заканчивается. Моя идея состояла в том, чтобы проверить (bytesRead > 0) и вызывать handler.BeginReceive() рекурсивно. Смотрите следующее:

Оригинал

if (bytesRead > 0) { 
    // There might be more data, so store the data received so far. 
    state.sb.Append(Encoding.ASCII.GetString(
     state.buffer,0,bytesRead)); 

    // Check for end-of-file tag. If it is not there, read 
    // more data. 
    content = state.sb.ToString(); 
    if (content.IndexOf("<EOF>") > -1) { 
     // All the data has been read from the 
     // client. Display it on the console. 
     Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", 
      content.Length, content); 
     // Echo the data back to the client. 
     Send(handler, content); 
    } else { 
     // Not all data received. Get more. 
     handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
     new AsyncCallback(ReadCallback), state); 
    } 
} 

Моя идея

if (received > 0) 
{ 
    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, received)); 
    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); 
} 
else 
    Send(handler, state.sb.ToString()); 

Если я заменить оригинальную часть с моей стороны программа прекращает работу. Я предполагаю, что клиент выполнил EndSend() в SendCallback() и заблокировал Thread после receiveDone.WaitOne() в StartClient(). Как я могу это передать? Нужно ли использовать токен, который определяет конец потока?

Есть ли еще хорошие примеры кода? Я только что нашел этих двоих.

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

Редактировать

Если я использую феллинг:

if (receive > 0) 
    state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, receive)); 
if (receive == StateObject.BufferSize) 
    state.listener.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state); 
else 
    Send(state.listener, state.sb.ToString()); 

Everyting работает отлично, я думаю. Это нормально? Или пропустить что-то?

Если я совмещаю два ifs, он не работает снова. Зачем?

if(receive > 0 || receive == StateObject.BufferSize) ->if(receive > 0) // Not working.

ответ

3

Проблема с вашей версией кода заключается в том, что вы начинаете BeginReceive всегда, но в одной ситуации, то есть когда получено 0 байтов.Фактически, конец передачи будет происходить в большинстве случаев, когда вы получаете количество данных, отличное от нуля, и меньше размера вашего буфера (а иногда, когда это точно размер буфера). Получение 0 байтов никогда не произойдет, потому что это означает без передачи rathen than конец передачи. Это предполагает, что отправитель заканчивает передачу, закрывая соединение.

Существует несколько способов, чтобы указать конец передачи:

  • отправитель закрывает соединение
  • специальный sequance отправляется (как EOF)
  • приемник получил то, что он ожидал (потому что он получил заголовок первого)

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

Сказав это, нет ничего, что помешало бы вам использовать комбинацию заголовков с EOF или заголовком, который будет содержать специальную маркировку последовательности начала записи.

0

Ситуация, когда вы можете определить, что поток сети закончилась, когда удаленный конец делает корректное завершение соединения. Это приведет к возврату EndRead. Это, конечно, не оптимальный подход, если вы хотите отправить данные обратно на удаленный конец. Вам, по сути, нужно будет создать новое соединение для клиента вместо повторного использования начального соединения. Это, безусловно, то, что вы можете сделать, но обычно это не сработает, например, если клиент находится за NAT-маршрутизатором.

Что делает пример кода при его проверке для EOF, обеспечивает соблюдение протокола уровня приложения. Базовый сетевой стек не понимает полезную нагрузку, которую он доставляет вашему приложению, и, следовательно, не может определить, что концептуальный поток закончился. Это то, на что ваша заявка несет ответственность. Проверка тега или токена - очень простой способ сделать это.

+0

Использование токена - общий способ? Я хотел бы сначала отправить длину контента, возможно ли это? Если полученный равен размеру буфера, я могу предположить, что есть больше данных? – hofmeister

+0

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

+0

Хорошо, спасибо. См. Мой ** Редактировать **. Fine? – hofmeister

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