Если вы отправляете данные через сокет за один вызов метода send (), будут ли они получены за один вызов метода receive ()? - PullRequest
2 голосов
/ 30 сентября 2010

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

Например, для передачи

mySocket.Send("SomeSpecificCommand")

Они предполагают, что принимающая сторона получит все данные за один вызов. Например:

Dim data(255) As Byte   
Dim nReceived As Long = s.Receive(data, 0, data.Count, SocketFlags.None)
Dim str As String = Encoding.ASCII.GetString(data, 0, n)
If str = "SomeSpecificCommand" Then
    DoStuff()
    ...

В приведенном выше примере терминатор не используется, поэтому программист полагается на тот факт, что реализация сокетов не допускается, например, для возврата «SomeSpecif» при первом вызове Receive () и «cCommand» в последующем вызове Receive (). (ПРИМЕЧАНИЕ. - В этом примере размер буфера больше ожидаемой строки).

Раньше я никогда не задумывался над этим и просто предполагал, что этот тип кодирования небезопасен и всегда использовал разделители. Я тратил впустую свое время (и циклы процессора)?

Ответы [ 5 ]

7 голосов
/ 30 сентября 2010

Нет гарантии, что все это прибудет в одно и то же время.Код (протокол приложения) должен учитывать возможность того, что данные из одной отправки могут поступать несколькими частями, или возможность того, что данные более чем из одной отправки могут быть получены за один прием.

1 голос
/ 01 октября 2010

При отправке данных по сети вы должны ожидать, что ваши данные будут фрагментированы по нескольким пакетам, и структурировать свой код и данные, чтобы справиться с этим. В примере, когда вы отправляете несколько байтов, все будет работать нормально ... пока вы не начнете отправлять большие пакеты.

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

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

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

1 голос
/ 30 сентября 2010

Короткие фрагменты данных, отправленные за один короткий вызов send (), будут обычно поступать за один вызов recv (), поэтому подобный код будет работать большую часть времени.Однако полагаться на это не гарантируется, и поэтому плохая практика.

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

С другой стороны, TCP может попытаться отправить его в одном пакете, но затем маршрутизатор в любом месте на пути к месту назначения может вернуться и сказать «этот пакет слишком большой!».Затем TCP разделит его на более мелкие пакеты.

0 голосов
/ 30 сентября 2010

Существует несколько типов розеток. TCP использует SOCK_STREAM, который не сохраняет границы сообщений. Сокеты SOCK_SEQPACKET сохраняют границы сообщений.

РЕДАКТИРОВАТЬ: SCTP поддерживает SOCK_STREAM и SOCK_SEQPACKET.

0 голосов
/ 30 сентября 2010

Нет, не стоит предполагать, что сервер (при условии, что ваш клиент) отправит вам только один ответ сокета.Сервер может быть запущен через список процедур, которые возвращают несколько результатов.Я продолжал бы читать из сокета, пока ничего не осталось, затем подождал несколько миллисекунд и протестировал снова.Если ничего не появляется, велика вероятность того, что сервер завершил отправку ответов.

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