|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Что-то я запутался в сокетах, требуется открыть соединение с сервером, отправить и получить оттуда несколько сообщений, закрыть коннект. В интернете написано что что-бы разделять сообщения надо к примеру вставлять header с длиной сообщения, и соответственно читать столько байтов, сколько указано в хэдере. Однако в примерах которые я нашел, включая на мсдн'е никаких заголовков нет https://msdn.microsoft.com/en-us/library/kb5kfec7(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx и для синхронного и для асинхронного типа чтения. И при этом моя тестовая программа тоже вроде как правильно работает с нижеследующим кодом: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.
Так нужны все эти заголовки или нет? ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 06:31 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
stenfordТак нужны все эти заголовки или нет? Нужны, но нет единого стандарта их оформления. В каждом случае выбирается какой-то вариант. Читай доки к своему серверу, там должно быть написано. TCP-соединение это поток байт, а не блоков данных (сообщений), поэтому тут нельзя ожидать что отправленное сообщение придет целиком, за раз может прийти как полсообщения, так и полтора или два. Для этого и надо знать размер, чтобы понять где конец каждого сообщения. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 07:43 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Код у тебя кривой Код: c# 1.
тут выйдешь из цикла не дочитав. Т.к. Available это сколько байт в буфере сокета, но не факт что там все отправленное, запросто может оказаться что там только часть, а остальное еще в дороге. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 07:49 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Dima TНужны, но нет единого стандарта их оформления. В каждом случае выбирается какой-то вариант. Читай доки к своему серверу, там должно быть написано. TCP-соединение это поток байт, а не блоков данных (сообщений), поэтому тут нельзя ожидать что отправленное сообщение придет целиком, за раз может прийти как полсообщения, так и полтора или два. Для этого и надо знать размер, чтобы понять где конец каждого сообщения. я не могу что-то нагуглить примеров как надо правильно читать с хэдерами, и приведенном мною примере у майкрософта тоже нет никаких хэдеров. У них получается неверный пример? while (senderSock.Available > 0) - это наверно вообще можно убрать т.к. насколько я понял senderSock.Receive(bytes) считает все в синхронном виде и в потоке больше ничего не останется. Если-же поток может содержать неполные сообщения, то непонятно как его надо модофицировать что-бы считывать до конца ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 08:07 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Примеры MS упрощенные. Там один запрос и сообщение несколько десятков байт. Тут можно быть уверенным что за раз прилетит целиком. По хорошему делается так: Отправка: 4 байта размер сообщения (BitConverter в помощь) само сообщение Прием: читаем 4 байта, получаем размер читаем в цикле кусками пока не получим полностью сообщение нужного размера. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 08:48 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Забыл упомянуть что данного алгоритма должны придерживаться обе стороны, и сервер и клиент. Если у тебя сервер стороннее приложение, то там должно быть описано как именно передается размер. Вполне возможно по другому, например жестко зашито что одно сообщение 100 байт, тогда размер не надо передавать. Это называется протокол обмена, соглашение по форматам, которые должны придерживаться обе стороны. Стандартов тут нет, решает разработчик как ему удобнее. Самый простой способ как выше написал, но бывают другие варианты. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 08:54 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Готовых примеров на C# нет, на С писал подобное. Просто совет чтоб надежно работало: исходи из того что может прийти сразу несколько сообщений за раз. Никто не запрещает отправить сразу два запроса, и потом ждать два ответа. С отправкой проблем нет, все проблемы в чтении. Например: прочитал размер 10000 байт, затем пришло 6000, а потом еще 6000. Вот тут твой код должен прочитать сначала первые 6000, а потом ждать оставшиеся 4000 и добавить их в конец, т.к. с 4001 это уже начало следующего сообщения. И перекодировку Encoding.GetEncoding() можно делать только после приема сообщения целиком, т.к. в UTF-8 некоторые символы (русские буквы например) кодируются двумя байтами и нет гарантии что в полученном куске последний символ целиком, а не половина. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 09:10 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Кроме заголовка могут использваться разнообразные разделители ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 09:23 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Dima TГотовых примеров на C# нет, на С писал подобное. Просто совет чтоб надежно работало: исходи из того что может прийти сразу несколько сообщений за раз. Никто не запрещает отправить сразу два запроса, и потом ждать два ответа. С отправкой проблем нет, все проблемы в чтении. Например: прочитал размер 10000 байт, затем пришло 6000, а потом еще 6000. Вот тут твой код должен прочитать сначала первые 6000, а потом ждать оставшиеся 4000 и добавить их в конец, т.к. с 4001 это уже начало следующего сообщения. И перекодировку Encoding.GetEncoding() можно делать только после приема сообщения целиком, т.к. в UTF-8 некоторые символы (русские буквы например) кодируются двумя байтами и нет гарантии что в полученном куске последний символ целиком, а не половина. по спецификации они используют хэдер с длиной. Но на тестовой программе все работает без хэдеров, возможно т.к. сообщения не сильно большой длины. Но до меня другое не доходит - вот выполнился следующий код: int bytesRec = senderSock.Receive(bytes); если сообщение там не полное - как его дочитать-то? Скажем хэдер говорит что длина 1000 байтов, я указанным выше методом считал 800. Надо дочитать 200 байтов, как это сделать? ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 09:47 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Указать в Socket.Receive() Примерно так: Код: c# 1. 2. 3. 4. 5. 6. 7. 8.
... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 09:57 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Почитал хэлп, не надо циклов. https://msdn.microsoft.com/ru-ru/library/8s4y8aff(v=vs.110).aspx Эта перегрузка требуется только для обеспечения приемного буфера.По умолчанию смещение буфера равно 0, размер установлен по умолчанию равным параметру буфера, а значение SocketFlags задано по умолчанию равным None. ... Если используется объект Socket, ориентированный на установление соединения, метод Receive будет выполнять чтение доступного объема данных, вплоть до максимального размера буфера.Если удаленный узел отключает соединение Socket с методом Shutdown и получены все доступные данные, метод Receive будет немедленно завершен и вернет нулевое число байтов. Если правильно понимаю, но Receive(bytes); будет тупо висеть и ждать данных пока полностью не заполнится массив bytes Тогда можно просто Код: c# 1. 2. 3.
... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 10:14 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
хм, так ведь если размер указан в хэдере, а хэдер это часть сообщения которое нельзя прочитать пока Receive(bytes) не вернет. Что-то здесь не так, если мы должны прочитать сначала хэдер, то указать размер массива до момента начала чтения не получится никак ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 12:00 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
stenfordхм, так ведь если размер указан в хэдере, а хэдер это часть сообщения которое нельзя прочитать пока Receive(bytes) не вернет. Что-то здесь не так, если мы должны прочитать сначала хэдер, то указать размер массива до момента начала чтения не получится никак Размер как именно указан? Т.е. размер размера какой? Если например фиксировано 4 байта, то сначала читай ровно 4 байта, а потом из прочитанного извлекай размер. Если переменная длина и разделитель, то читай побайтно, пока разделитель не прочитаешь. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 12:11 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
это как, прочитать поток 2 раза? Код: c# 1. 2. 3. 4. 5.
т.е. первое чтение только считает 4 байта, а второе уже само сообщение нужной длины? ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 12:56 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
а кстати такой момент, ведь на данный момент тестовое приложение работает с произвольным размером массива (1024 байта), и результаты дает правильные. Так может действительно проще просто указывать размер массива заведомо больше чем самое больше сообщение, ну скажем 5000 байтов, и все будет работать безо всяких чтений хэдеров? Сейчас-же работает с массивом байтов который больше чем тестовое сообщение ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 13:06 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Именно так. Только проверки размера прочитанного добавь. Там не всегда исключение, по хэлпу может выпасть вернув 0 если та сторона закроет соединение. Еще раз TCP-соединение это поток байт, а не блоков данных (сообщений), поэтому какими порциями прочитаешь, такие сообщения и получатся. Все что гарантирует TCP это то что отправленное будет принято в той же последовательности. Если понятнее надо - считай что размер это часть сообщения, а отдельное сообщение содержащее размер следующего за ним сообщения. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 13:08 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Опечатка, так правильно Dima TЕсли понятнее надо - считай что размер это не часть сообщения, а отдельное сообщение содержащее размер следующего за ним сообщения. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 13:10 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
stenfordТак может действительно проще просто указывать размер массива заведомо больше чем самое больше сообщение, ну скажем 5000 байтов, и все будет работать безо всяких чтений хэдеров? ХЗ как оно у тебя работает, согласно хэлпу должно висеть и ждать пока буфер полностью не заполнится или сервер не закроет соединение. ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 13:14 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
ок, спасибо, поэксперементирую еще ... |
|||
:
Нравится:
Не нравится:
|
|||
20.07.2016, 13:23 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
думаю в русском переводе косяк, в оригинальной английской версии сказано следующее: If you are using a connection-oriented Socket, the Receive method will read as much data as is available , up to the size of the buffer. https://msdn.microsoft.com/en-us/library/8s4y8aff(v=vs.110).aspx это означает что метод не будет висеть пока буфер не заполнится, а считает сколько доступно, и вернет результат. Совершенно непонятно каким образом он узнает что мессадж закончился. Может все-таки эти сокеты умные, и сами определяют конец сообщения и начало следующего? На тестовом сервере выполняется следующий метод int bytesSend = handler.EndSend(ar); Т.е. возможно конец сообщения как-то помечается. С другой стороны на реальном сервере используется операционка IBM и как они там отправляют сообщения совершенно неизвестно, известно только что включен хэдер с длиной ... |
|||
:
Нравится:
Не нравится:
|
|||
21.07.2016, 04:29 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
stenford, Ну нету у tcp сокета никаких сообщений и искуственного интеллекта Хоть об стену убеся - нету ... |
|||
:
Нравится:
Не нравится:
|
|||
21.07.2016, 07:30 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
stenfordдумаю в русском переводе косяк, в оригинальной английской версии сказано следующее: If you are using a connection-oriented Socket, the Receive method will read as much data as is available , up to the size of the buffer. https://msdn.microsoft.com/en-us/library/8s4y8aff(v=vs.110).aspx это означает что метод не будет висеть пока буфер не заполнится, а считает сколько доступно, и вернет результат. Если так то использовать вариант с циклом 19429924 Затестить просто, дай больше буфер, не заполнит, значит так как ты пишешь. Оно именно так на уровне API ОС, ждет пока не пришло что-нибудь, поэтому приходится всегда циклы делать, подумал что в С# это порешали. stenfordСовершенно непонятно каким образом он узнает что мессадж закончился. Может все-таки эти сокеты умные, и сами определяют конец сообщения и начало следующего? Сокеты не умные, там все просто: сообщение, которое ты даешь на отправку дописывается в конец буфера отправки (его создает ОС для каждого сокета), затем это разбивается на IP-пакеты (~1460 байт) и пакеты отправляются в сеть. По приходу содержимое пакетов дописывается в буфер приема и читающий софт читает из буфера. .Available это и есть сколько байт на текущий момент в буфере. При блокирующем чтении ожидающий разблокировывается как только хоть что-то упадет в буфер. Если задержки между отправками большие и данных мало, то создается иллюзия что "сокеты умные", т.к. получатель успевает обработать содержимое каждого пакета отдельно. Но если в буфер приема успеют прийти два пакета (от разных сообщений), то ты прочитаешь их вместе, или например большое сообщение уйдет тремя пакетами, третий немного отстанет, то ты прочитаешь сначала содержимое первых двух, а следующим чтением остальное. Поэтому без размера тут никак. Другой вариант - использовать разделители. В твоем случае, раз у тебя работа по принципу запрос-ответ, два ответа вместе ты никак не получишь, т.к. второй запрос после чтения ответа на первый, но получить ответ двумя чтениями это вполне возможная ситуация (см. выше про несколько IP-пакетов и последний отстал). ... |
|||
:
Нравится:
Не нравится:
|
|||
21.07.2016, 07:56 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Dima TЕсли так то использовать вариант с циклом 19429924 Затестить просто, дай больше буфер, не заполнит, значит так как ты пишешь. Оно именно так на уровне API ОС, ждет пока не пришло что-нибудь, поэтому приходится всегда циклы делать, подумал что в С# это порешали. да, спасибо, я уже разобрался, то первое сообщение про циклы и было правильным, надо считать хэдер, обьявить массив байтов по уазанному в хэдере размеру и висеть в цикле пока не получим все байты. Получился такой код, который успешно работает с реальным сообщением от IBM сервера: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
... |
|||
:
Нравится:
Не нравится:
|
|||
21.07.2016, 08:09 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
правда протестить как в цикле крутится наверно неполучится раз он все сообщение сразу получает, но вроде должно теперь работать ... |
|||
:
Нравится:
Не нравится:
|
|||
21.07.2016, 08:11 |
|
Сокеты - несколько сообщений в одном коннекте
|
|||
---|---|---|---|
#18+
Ерунду написал, тут зациклится если придет в три чтения. Код: c# 1. 2. 3. 4.
Смотри мой пример, там три переменных: need - размер ожидаемого сообщения pos - смещение куда читать очередную порцию recv - сколько прочиталось за раз ... |
|||
:
Нравится:
Не нравится:
|
|||
21.07.2016, 08:17 |
|
|
start [/forum/topic.php?fid=20&fpage=58&tid=1400457]: |
0ms |
get settings: |
10ms |
get forum list: |
14ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
38ms |
get topic data: |
12ms |
get forum data: |
3ms |
get page messages: |
63ms |
get tp. blocked users: |
2ms |
others: | 16ms |
total: | 166ms |
0 / 0 |