SendTo виснет
, UDP Winsock
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.123] |
|
|
Соблюдайте общие правила форума
MSDN Library
FAQ раздела
Поиск по разделу
Как правильно задавать вопросы
SendTo виснет
, UDP Winsock
|
Сообщ.
#1
,
|
|
|
|
Здравствуйте, прошу помочь разобраться.
Среда Delphi7, WinXP. По примеру из UDP Socket пробую отправить данные на локальную машину, с установленой Win7(домашняя сеть, компьютеры подлючены к шлюзу/сетевому коммутатору d-link): ![]() ![]() procedure TForm1.Button1Click(Sender: TObject); var FWSAData: TWSAData; FSockAddr: TSockAddrIn; FSocket: HWND; data:array of byte; begin setlength(data,3000); WSAStartup($0101, FWSAData); FSocket := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); with FSockAddr do begin sin_addr.S_addr := Inet_Addr('192.168.1.56'); sin_port := htons(5000); sin_family := AF_INET; end; SendTo(FSocket, Data[0], Length(Data), 0, FSockAddr, SizeOf(FSockAddr)); ShowMessage(SysErrorMessage(WSAGetLastError)); closesocket(FSocket); WSACleanup; end; Если размер данных не превышает 1 кБайт, sendto отрабатывает "мгновенно" c любыми адресами(локальными, глобальными). Если больше, начинаются варианты: 1. адрес локальный, несуществующий, sendto некоторое время "думает" и завершается с WSAGetLastError=00. 2. адрес доставки локальный, реально существует, но отсуствует в arp-таблице, SendTo виснет надооооооооооооооооолго, и когда уже ничего вообще от неё не ждёшь, отбивается с WSAGetLastError=10004(Операция блокирования прервана вызовом WSACancelBlockingCall) Делаю Ctrl+F2(или дожидаюсь, пока sendto "отпустит"), запускаю повторно - операция выполнена успешно. Cмотрю arp -a - комп в таблице. Почему так происходит? Просветите, пожалуйста. В гугле ответа не нашёл. Спасибо. |
|
Сообщ.
#2
,
|
|
|
|
У протокола есть ограничения. в педивикии про это упоминается.
https://ru.wikipedia.org/wiki/UDP#.D0.94.D0....BB.D0.BA.D0.B8 |
|
Сообщ.
#3
,
|
|
|
|
Какое именно ограничение вызывает описанное поведения sendto?
|
|
Сообщ.
#4
,
|
|
|
|
Цитата Prince @ 1. адрес локальный, несуществующий, sendto некоторое время "думает" и завершается с WSAGetLastError=00. К сожалению, такого кода sendto() не может выдать. Посмотрите MSDN о кодах ошибки sendto(). Принципиально - sendto() пошлет UDP-пакет по любому адресу. потому что эта функция лишь копирует датаграмму в буфер передачи сокета. Далее на локальном компе формируется IP-пакет с указанным IP-адресом и формируется ARP-запрос на получение МАС-адреса компа-получателя - например сервера DNS. Если получатель в локальной сети, и его IP фейковый, то дальнейшие действия как вы правильно написали упираются в получение записи из арп таблицы. Если такой записи нет, то модуль арп пытается несколько раз с увеличенным таймаутом послать очередной широковещательный запрос WHO HAS. Это может длиться например до 29-ти секунд(BSD) или более (зависит от ОС), после чего модуль говорит о том, что он не может найти такой МАС.Как эта ошибка будет зафиксирована на пользовательском уровне - это уже дело взаимодействия уровней. Вот примерно так. Так что подвисание при отсутствии нужной записи в арп-таблице вполне очевидно. |
|
Сообщ.
#5
,
|
|
|
|
Цитата К сожалению, такого кода sendto() не может выдать. Какое значение должна возвращать WSAGetLastError после нормального заверршение sendto? Цитата Если получатель в локальной сети, и его IP фейковый, то дальнейшие действия как вы правильно написали упираются в получение записи из арп таблицы. Если такой записи нет, то модуль арп пытается несколько раз с увеличенным таймаутом послать очередной широковещательный запрос WHO HAS. Это может длиться например до 29-ти секунд(BSD) или более (зависит от ОС), после чего модуль говорит о том, что он не может найти такой МАС. Если фейковый, то sendto подвисает буквально на секунду-другую. А вот если реальный, и в arp-таблице, на момент вызова sendto, его нет, sendto виснет наглухо... Да, и всё это усугубляется зависимостью от размера данных. Я не понимаю. |
|
Сообщ.
#6
,
|
|
|
|
Цитата Prince @ Какое значение должна возвращать WSAGetLastError после нормального заверршение sendto? Цитата If no error occurs, sendto returns the total number of bytes sent, which can be less than the number indicated by len. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError. Т.е.при нормальном завершении возвращает обычное целое число, которое обозначает, сколько байтов она реально скопировала в буфер передачи сокета. Кстати, а сколько вы отсылаете вот этим вызовом? SendTo(FSocket, Data[0], Length(Data), 0, FSockAddr, SizeOf(FSockAddr)); Ноль что ли? Или один первый байт? Т.е. что означает Data[0], если data:array of byte; setlength(data,3000); Если один - то Length(Data) как то... Может я в паскале и не очень силен, но тут мне что то непонятно. Цитата Prince @ А вот если реальный, и в arp-таблице, на момент вызова sendto, его нет, sendto виснет наглухо... По идее так не должно быть. Ведь UDP к ARP не имеет никакого отношения. Цитата Prince @ А вот если реальный, и в arp-таблице, на момент вызова sendto, его нет, sendto виснет наглухо... Что значит - наглухо? На всю жизнь??? Есть правда и такой вариант - дельфовые сетевые компоненты порой ведут себя глючно, может это один из глюков - "нет в арп - не буду работать"??? Цитата Prince @ Да, и всё это усугубляется зависимостью от размера данных. Структура WSADATA при WSAStartup возвращает поле MAX UDP Datagram, в которой записан максимальный размер udp-пакета допускаемый данной системой. Гляньте это поле. |
|
Сообщ.
#7
,
|
|
|
|
Похоже, это у sendto аллергия на касперский NDIS Filter.
|
|
Сообщ.
#8
,
|
|
|
|
Цитата Prince @ Похоже, это у sendto аллергия на касперский NDIS Filter. Аааааааааааааааа!!! Мого быть...про файер я как-то не подумал... Про это что означает Data[0] Я так понял, вы указали только адрес начала массива. Тогда все ок. |
|
Сообщ.
#9
,
|
|
|
|
Цитата Т.е.при нормальном завершении возвращает обычное целое число, которое обозначает, сколько байтов она реально скопировала в буфер передачи сокета. Мне кажется, вы меня не поняли. sendto в случае нормального завершения возвращает количество байт, при этом WSAGetLastError=0(по крайней мере, у меня так). Цитата SendTo(FSocket, Data[0], Length(Data), 0, FSockAddr, SizeOf(FSockAddr)); Начиная с 0-го элемента, размер равен размеру массива (3000 байт). Цитата По идее так не должно быть. Ведь UDP к ARP не имеет никакого отношения. Вот я и не понял. Всюду пишут, что UDP не требует установления соединения, хоть на деревню дедушке отправь - всё равно улетит. И тут такое. Цитата Что значит - наглухо? На всю жизнь??? На ооооочень долго, как минимум. Потом socket_error с WSAGetLastError=10004(Операция блокирования прервана вызовом WSACancelBlockingCall). Было, что я так и не смог дождаться WSACancelBlockingCall. Т.е., можно сказать, на всю жизнь. Цитата Структура WSADATA при WSAStartup возвращает поле MAX UDP Datagram, в которой записан максимальный размер udp-пакета допускаемый данной системой. Гляньте это поле. iMaxUpdDg = 65467 |
|
Сообщ.
#10
,
|
|
|
|
Цитата Prince @ Мне кажется, вы меня не поняли. sendto в случае нормального завершения возвращает количество байт, при этом WSAGetLastError=0(по крайней мере, у меня так). Да нет все понятно, за исключением "завершается с WSAGetLastError=00." Вот про два нуля мне и не было понятно Цитата Prince @ На ооооочень долго, как минимум. Потом socket_error с WSAGetLastError=10004(Операция блокирования прервана вызовом WSACancelBlockingCall). Было, что я так и не смог дождаться WSACancelBlockingCall. Т.е., можно сказать, на всю жизнь. Ну что тут можно сказать... Во первых, по моему мнению, sendto()нельзя причислить к блокирующим функциям. Операция копирования из буфера приложения в буфер отправки сокета выполняется ровно столько, сколько затрачивает времени ассемблерный код. Единственный вариант, когда эта функция может реально блокировать - это отсутствие места в буфере отправки. Поэтому я бы советовал такое - определить размер буфера отправки сокета, и при необходимости тупо его увеличить. Как может не оказаться места в буфере? Один вариант - сеть перегружена и карточка не смогла отправить данные, а тут опять sendto()/ В вашем варианте этого вроде бы не должно быть. Или задержка арп-ответа - может дать такой эффект. Или глюк дельфи... Фиг его знает. Во всяком случае я бы увеличил размер буфера. |
|
Сообщ.
#11
,
|
|
|
|
Саппорт касперского:
Цитата Не рекомендуется отключать использование Kaspersky Anti-Virus NDIS Filter снятием флага в окне свойств Local Area Connection. NDIS Filter переходит в такой режим работы, который может привести к некорректной работе всей операционной системы (например, к падению в "синий экран"). А я уже такую операцию проделал, потом вернул галочку на место. И теперь не могу воспроизвести глючную ситуацию! То ли касперский стал работать как нужно, то ли я его сломал и он больше пакеты не перехватывает. ![]() Цитата Поэтому я бы советовал такое - определить размер буфера отправки сокета, и при необходимости тупо его увеличить. 8192. Добавлено Вопрос как бы и решён и не решён. |
|
Сообщ.
#12
,
|
|
|
|
Понятно.
Уже как бы поздновато, обсудим позже... |
|
Сообщ.
#13
,
|
|
|
|
Oleg2004
Цитата Oleg2004 @ SendTo(FSocket, Data[0], Length(Data), 0, FSockAddr, SizeOf(FSockAddr)); Ноль что ли? Или один первый байт? В данном случае передается как ссылка на нулевой элемент. Это такая особенность паскаля, неявная передача по ссылке. Prince Вроде подобные симтомы были от файрвола касперского. Что-то он не корректно большьшие максимальные пакеты UDP обрабатывал. Предлагаю ограничить размер пакета 1434 байт и пробовать увеличивать пока не пойдут сбои или уменьшать. Цитата Oleg2004 @ По идее так не должно быть. Ведь UDP к ARP не имеет никакого отношения. Я уже ничему не удивляюсь. Для определения интерфейса с которого отправлять есть 4 механизма. Метка приоритета, IP адрес и маска, ARP адрес, прочий способ. На разных ОС и в разных железках оно работает по разному. Не говоря уже про межсетевойэкран(фаервол), который определяет маршрут пакета так как ему угодно. |
|
Сообщ.
#14
,
|
|
|
|
Цитата Pavia @ Я уже ничему не удивляюсь. А я как раз таки постоянно удивляюсь - ну как это можно такое глюкавое выпускать??? |
|
Сообщ.
#15
,
|
|
|
|
Цитата Вроде подобные симтомы были от файрвола касперского. Вот я уже почти поверил, что это фильтр касперского виноват. Был. Только у меня не кис, а антивирус. Цитата Предлагаю ограничить размер пакета 1434 байт и пробовать увеличивать пока не пойдут сбои или уменьшать. Да уже всё, не могу воспроизвести сбой. Я на него столько времени угрохал. Может, завтра снова появится(с надеждой).Было так: отправляю больше 1 кБайта(>1024 байт) - глюк. Если <=1024) - всё нормально. |
|
Сообщ.
#16
,
|
|
|
|
Цитата Prince @ Да уже всё, не могу воспроизвести сбой. Я на него столько времени угрохал. Может, завтра снова появится(с надеждой).Странные мы все-таки люди... сами создаем траблы и сами их убиваем. А по убиенному траблу потом скучаем Хотя прям скажем трабл неординарный был... |