oktonion
Постоялец
Offline
|
|
« : 14-05-2013 19:29 » |
|
Доброго времени суток.
Есть вопрос по сокетам в unix.
Задача: Хотелось бы узнать размер первого сообщения\пакета в очереди, не считывая его.
Замечательный Help подсказывает что функция recvfrom с параметром MSG_PEEK возвращает "number of bytes received", что вроде и требуется. Но методом простой проверки можно убедиться, что возвращает данная функция количество байт считанных в буфер, т.е. если буфер был выделен меньше чем размер пришедшего пакета, то вернет она ровно "размер буфера", а не сколько реально в пакете байт лежит. Конечно есть вариант выделить буфер максимально возможного размера пакета, считывать в него и не жалеть память. Но как-то это не красиво. К сожалению функции которая просто возвращала размер первого пакета в очереди не нашел, подскажите пожалуйста оную или какие еще могут быть варианты решения.
|
|
|
Записан
|
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #1 : 15-05-2013 05:14 » |
|
oktonion, можно попробовать ioctl(fd, FIONREAD, &numbytes);
она точно работает для AF_INET-сокетов и терминальных устройств, вполне возможно, что будет работать и для UNIX-сокетов.
|
|
« Последнее редактирование: 15-05-2013 05:19 от darkelf »
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #2 : 15-05-2013 14:26 » |
|
darkelf, спасибо за столь оперативный ответ. Действительно работает, только вот теперь не очень понятно почему она возвращает всегда ровно на 16 байт больше чем длина сообщения. Тестировал правда на 1 машине, возможно результат на другой был бы иным, нет возможности проверить.
|
|
|
Записан
|
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #3 : 15-05-2013 15:29 » |
|
oktonion, возможно я не совсем правильно понял Ваш первый вопрос (у вас сокет типа AF_UNIX/AF_LOCAL?), скажите пожалуйста, как Вы создаёте сокет? Если у Вас SOCK_STREAM, то там нет разбиения на сообщения, соответственно функция сообщает, сколько на текущий момент доступно байт (возможно из нескольких сообщений).
|
|
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #4 : 16-05-2013 14:27 » |
|
Да, наверное я вас ввел в заблуждение заголовком "Unix sockets", сокеты используются AF_INET, вопрос же конкретно про датаграммные сокеты.
|
|
|
Записан
|
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #5 : 16-05-2013 15:32 » |
|
приведите минимальный исходный код, а так-же на какой системе Вы проверяете, к сожалению unix-ы бывают разные и, не смотря на одинаковый программный интерфейс, всё-таки различаются.
|
|
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #6 : 16-05-2013 19:41 » |
|
Создаю: socketDescr = socket(AF_INET, SOCK_DGRAM, 0); Принимаю: msgLen = recvfrom(socketDescr, buffer, bufSize, 0, (sockaddr*)sockAddres, (socklen_t*)&addrSize); ioctl(socketDescr, FIONREAD, &numBytes);
printf("Msg length:%d;%d\n", msgLen, numBytes);//debug В результате при отправке сообщения длиной 800 байт, recvfrom возвращает 800, numBytes же становится 816. Работает все это под QNX шестым.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #7 : 16-05-2013 20:35 » |
|
Похоже это фича QNX: размер включает IP/UDP заголовки. http://developerweb.net/viewtopic.php?id=4237On QNX, when I actually try to use bytesP, I know that I need to subtract 16 bytes for the IPv4 socket address structure. On Linux, conversely, I don't need to subtract the 16 bytes. Или нет... Попробуй использовать SIOCINQ.
|
|
« Последнее редактирование: 16-05-2013 20:49 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #8 : 21-05-2013 20:01 » |
|
RXL, дело в том, что в QNX нет SIOCINQ как такового. И на сколько я понял, фактически этот флаг не дает никакого отличного от FIONREAD эффекта. 16 байт действительно похоже на заголовок отводится. Что касается ioctl, есть еще одна проблема с данной функцией: с помощью нее нельзя отличить отсутствие пакета от пустого пакета (частая практика в UDP например том же самом). И для решения данной проблемы не вижу иного способа как использовать select/poll. Если есть какие то более простые решения, то с радостью бы их применил.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #9 : 22-05-2013 03:55 » |
|
Я проще не нашел. Контролировать IO все равно нужно, а определение размеров пакета вызывать непосредственно перед чтением. Лучшее решение, не требующее таких изысков: в пересылаемой структуре включать в начало поле длины или, если пересылается набор и общий заголовок — включать счетчик записей, или пересылать записи фиксированного размера. В этом случае можно пользоваться read() и забить на пакетность.
|
|
« Последнее редактирование: 22-05-2013 03:59 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #10 : 22-05-2013 06:06 » |
|
как вариант, буфер выделять с запасом, для дейтаграммных сокетов всё-равно пакет может быть не более 64К, соответственно, можно работать без всяких ioctl(), просто всегда принимая в буфера равный 64К.
|
|
« Последнее редактирование: 22-05-2013 09:15 от darkelf »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #11 : 22-05-2013 07:05 » |
|
darkelf, ты уверен, что прочитается ровно один пакет, а не весь скопившийся буфер?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #12 : 22-05-2013 08:52 » |
|
RXL, насколько я помню - udp должен отдавать по пакетам, т.е. до одного пакета за раз, может меньше, но в случае неполной вычитки та часть, что осталась отбрасывается. Проверил у Ричарда В.Стивенса "Разработка сетевых приложений": он тоже так считает: "...когда процесс вызывает функцию recvfrom() очередная дейтаграмма возвращается процессу..." Дейтаграммы буферизируются, но при этом границы между ними остаются. В своих примерах он обычно и выполняет приём в буфер какой-то максимальной длины. Максимальный объём буферезированных ядром данных можно установить через setsockopt(s, SO_RCVBUF). Хотя MSDN в данном случае не показатель, но там более подробно расписано:
For message-oriented sockets, data is extracted from the first enqueued message, up to the size of the buffer supplied. If the datagram or message is larger than the buffer supplied, the buffer is filled with the first part of the datagram, and recvfrom generates the error WSAEMSGSIZE. For unreliable protocols (for example, UDP) the excess data is lost.
|
|
« Последнее редактирование: 22-05-2013 09:18 от darkelf »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #13 : 22-05-2013 13:00 » |
|
Одно НО: в своих книгах Стивенс QNX не описывал. Тут тоже может быть разногласие и надо проверять опытным путем.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #14 : 22-05-2013 13:07 » |
|
Насколько я знаю, в QNX используется сетевой стек от NetBSD, с обеспечением аналогичного прикладного интерфейса, в том числе и не совсем документированного.
Добавлено через 4 минуты и 36 секунд: кстати, у Стивенса, но в английской версии и новом издании нашел вот что:
Some implementations support the FIONREAD command of ioctl. The third argument to ioctl is a pointer to an integer, and the value returned in that integer is the current number of bytes on the socket's receive queue (p. 553 of TCPv2). This value is the total number of bytes queued, which for a UDP socket includes all queued datagrams. Also be aware that the count returned for a UDP socket by Berkeley-derived implementations includes the space required for the socket address structure containing the sender's IP address and port for each datagram (16 bytes for IPv4; 24 bytes for IPv6).
|
|
« Последнее редактирование: 22-05-2013 13:12 от darkelf »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #15 : 22-05-2013 13:26 » |
|
Интересный аспект. Ты наверно первый, кто сие прочел, т.к. в сети, где обсуждались эти 16 байт, книгу не цитировали.
Что такое «новое издание»? Стивенс умер в 1999-м и сам дописать не мог. В Вики такая библиография: 1990 - UNIX Network Programming - ISBN 0-13-949876-1 1992 - Advanced Programming in the UNIX Environment - ISBN 0-201-56317-7 1994 - TCP/IP Illustrated, Volume 1: The Protocols - ISBN 0-201-63346-9 1995 - TCP/IP Illustrated, Volume 2: The Implementation (with Gary R. Wright) - ISBN 0-201-63354-X 1996 - TCP/IP Illustrated, Volume 3: TCP for Transactions, HTTP, NNTP, and the UNIX Domain Protocols - ISBN 0-201-63495-3 1998 - UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI - ISBN 0-13-490012-X 1999 - UNIX Network Programming, Volume 2, Second Edition: Interprocess Communications - ISBN 0-13-081081-9 2003 - UNIX Network Programming Volume 1, Third Edition: The Sockets Networking API - ISBN 0-13-141155-1 (with Bill Fenner, and Andrew M. Rudoff) 2005 - Advanced Programming in the UNIX Environment, Second Edition - ISBN 0-321-52594-9 (with Stephen A. Rago) 2011 - TCP/IP Illustrated, Volume 1: The Protocols (2nd Edition) - ISBN 0-321-33631-3 (with Kevin R. Fall)
|
|
« Последнее редактирование: 22-05-2013 13:30 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #16 : 22-05-2013 13:36 » |
|
новая это 2003 - UNIX Network Programming Volume 1, Third Edition: The Sockets Networking API - ISBN 0-13-141155-1 (with Bill Fenner, and Andrew M. Rudoff), обновлённая уже после его смерти. у меня есть chm файл, там это раздел 14.7 How Much Data Is Queued? Вроде был издан и перевод http://www.ozon.ru/context/detail/id/2881910/
|
|
« Последнее редактирование: 22-05-2013 13:42 от darkelf »
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #17 : 22-05-2013 15:13 » |
|
Я проще не нашел. Контролировать IO все равно нужно, а определение размеров пакета вызывать непосредственно перед чтением.
Я тоже. Остановился на этом варианте пока. как вариант, буфер выделять с запасом, для дейтаграммных сокетов всё-равно пакет может быть не более 64К, соответственно, можно работать без всяких ioctl(), просто всегда принимая в буфера равный 64К.
Мое первое же сообщение : Конечно есть вариант выделить буфер максимально возможного размера пакета, считывать в него и не жалеть память. Но как-то это не красиво.
Одно НО: в своих книгах Стивенс QNX не описывал. Тут тоже может быть разногласие и надо проверять опытным путем.
Проверка опытным путем показала что в UDP сокетах QNX очередь именно пакетная, то есть recvfrom считывает либо пакет из очереди целиком, либо считает что пакета нет, если пришел только кусок от полного сообщения. Кстати на счет 16 байт я немного не то написал, все же заголовок UDP это только 8 байт, то есть еще 8 - это IP видимо. Что и подтверждает товарищ Стивенс: кстати, у Стивенса, но в английской версии и новом издании нашел вот что:
Some implementations support the FIONREAD command of ioctl. The third argument to ioctl is a pointer to an integer, and the value returned in that integer is the current number of bytes on the socket's receive queue (p. 553 of TCPv2). This value is the total number of bytes queued, which for a UDP socket includes all queued datagrams. Also be aware that the count returned for a UDP socket by Berkeley-derived implementations includes the space required for the socket address structure containing the sender's IP address and port for each datagram (16 bytes for IPv4; 24 bytes for IPv6).
Жаль что не могу проверить на IPv6, но думаю все подтвердится.
|
|
|
Записан
|
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #18 : 22-05-2013 15:25 » |
|
как вариант, буфер выделять с запасом, для дейтаграммных сокетов всё-равно пакет может быть не более 64К, соответственно, можно работать без всяких ioctl(), просто всегда принимая в буфера равный 64К.
Мое первое же сообщение : Конечно есть вариант выделить буфер максимально возможного размера пакета, считывать в него и не жалеть память. Но как-то это не красиво.
Просто, как оказывается, вариант с максимальным буфером, вполне оказывается, прошу прощения за тавтологию, вполне себе каноническим, без дополнительных запросов к ядру/сетевому стеку и без проблем с переносимостью.
|
|
|
Записан
|
|
|
|
|