|
|
  |
Не могу нормально принять данные по UDP, Помогите, пожалуйста. |
|
|
|
Jul 3 2011, 19:29
|

Дима
    
Группа: Свой
Сообщений: 1 683
Регистрация: 15-12-04
Из: Санкт-Петербург
Пользователь №: 1 486

|
Здравствуйте все! Я соорудил некое устройство на W5100 в связке с ПЛИС, которое обменивается с компьютером по UDP. На компьютере работает моя самодельная программа, написанная в Delphi 7. Она работает с UDP через WinAPI так, как написано в книге А.Б. Григорьева "О чём не пишут в книгах по Delphi", стр. 204 и далее. Или я думаю, что она так работает. Пока надо было гонять туда-сюда по два-три пакета, всё было хорошо. Сейчас я отправляю через W5100 подряд 256 пакетов размером поменьше MTU с интервалом 4 мс. Все эти пакеты появляются в компьютере, их видно в сниффере WireShark, и содержимое у них правильное. Но моя программа, приняв 5 первых пакетов, затем примерно полсекунды не видит приходящих пакетов. А потом она нормально принимает все оставшиеся, больше сотни. Содержимое каждого пакета - байты с его номером, так что легко понять, чего не хватает, а что пришло. То есть, как я понимаю, системе хватает быстродействия, чтобы принимать пакеты каждые 4 мс. Причём даже по 2 штуки за 4 мс: я пробовал слать пакеты больше MTU, они разбивались на пары внутри W5100 и нормально доходили, и тогда до пропадания программа нормально принимала 9 пакетов. Как бороться с пропаданием пакетов после начала передачи? Очень прошу объяснить как можно проще, потому что я не разбираюсь в сетевых вопросах.
--------------------
|
|
|
|
|
Jul 3 2011, 19:45
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (DSIoffe @ Jul 3 2011, 21:29)  На компьютере работает моя самодельная программа, написанная в Delphi 7. Она работает с UDP через WinAPI так, как написано в книге А.Б. Григорьева "О чём не пишут в книгах по Delphi", стр. 204 и далее. Нет не малейшего желания читать, то, что мог кто-то на Дельфи написать, но сокетами у Win проблем,естественно нет. Просто нужно воспользоваться возможностями операционной системы, например, тем-же select() для отслеживания состояний сокета.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 3 2011, 20:08
|

Дима
    
Группа: Свой
Сообщений: 1 683
Регистрация: 15-12-04
Из: Санкт-Петербург
Пользователь №: 1 486

|
Я не по событию. Я проверяю готовность сокета: if select(0, @SocketSet, nil, nil, @Timeout) = SOCKET_ERROR а потом проверяю, оставила ли функция select мой сокет в множестве сокетов: if FD_ISSET(MySocket, SocketSet) Если оставила, значит, в сокете есть данные, если нет, то проверяю ещё раз, до победного конца или до выхода по тайм-ауту.
--------------------
|
|
|
|
|
Jul 7 2011, 11:00
|
Участник

Группа: Участник
Сообщений: 57
Регистрация: 8-12-04
Пользователь №: 1 403

|
Цитата(DSIoffe @ Jul 3 2011, 23:29)  Все эти пакеты появляются в компьютере, их видно в сниффере WireShark, и содержимое у них правильное. Но моя программа, приняв 5 первых пакетов, затем примерно полсекунды не видит приходящих пакетов. Пакеты UDP у вас корректные? Контрольные суммы правильные? Длины TCP и UDP частей соответствуют реальным? MAC, IP, Port верно указаны?Если что не так, то сокет прибъет некорректный пакет, а снифер съест любой - даже самый кривой... И еще вероятность небольшая, но может мешает фаервол. Снифер ловит до фаервола, поэтому там все пакеты. Конечно обычно фаер должен либо блокировать все, либо все пропускать, но ... может какая-то странная защита от flood или быстродействие все-таки. Поправьте скорострельность в настройках - пусть пакеты летят раз в секунду. Что-то изменится?
|
|
|
|
|
Jul 11 2011, 07:06
|

Дима
    
Группа: Свой
Сообщений: 1 683
Регистрация: 15-12-04
Из: Санкт-Петербург
Пользователь №: 1 486

|
Пакеты правильные, потому что их формирую не я, а умная штуковина W5100. Они же, в основном, приходили нормально. А не хватало всего двух строчек: CODE RcvBufLen:= 82000000; //больше не должно придти setsockopt(MySocket,SOL_SOCKET,SO_RCVBUF,@RcvBufLen,4); Профессиональный программист посидел пару часов с моим кодом. Эти строчки задают размер буфера, в который складываются поступающие данные. Итого, вот весь код для приёма данных по UDP с использованием только WinAPI. А то я уже с перепугу чуть не начал изучать WinPCAP. CODE //Объявлено глобально: var MySocket: TSocket; SockAddr, DeviceSock: TSockAddr; // Буфер для получения сообщения. Размер равен максимальному размеру UDP-дейтаграммы: Buffer: array[0..65506] of Byte; // Адрес, с которого пришло сообщение RecvAddr: TSockAddr; RecvLen, AddrLen: Integer;
//Выполняется один раз при создании главной формы приложения //Здесь и далее работа программы протоколируется в MemoLog типа TMemo. //Инициализация библиотеки сокетов (стр. 204 у Григорьева): err := WSAStartup($0101,WSData); if err = 0 then MemoLog.Lines.Add('Инициализация библиотеки сокетов: OK') else begin err := WSAGetLastError; MemoLog.Lines.Add('Ошибка инициализации библиотеки сокетов с кодом'+IntToStr(err)) end; //Открытие сокета (стр. 205 у Григорьева): MySocket := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if MySocket = INVALID_SOCKET then MemoLog.Lines.Add(GetErrorString) else begin MemoLog.Lines.Add('Открытие сокета: OK'); SocketLen:=SizeOf(Integer); GetSockOpt(MySocket,SOL_Socket,SO_SndBuf,@SocketVal,SocketLen); MemoLog.Lines.Add(' Размер передающего буфера '+IntToStr(SocketVal)+' байтов'); GetSockOpt(MySocket,SOL_Socket,SO_RcvBuf,@SocketVal,SocketLen); MemoLog.Lines.Add(' Размер приёмного буфера '+IntToStr(SocketVal)+' байтов') end; //Привязка сокета к адресу (стр. 206 у Григорьева): SockAddr.sin_family := PF_INET; IPstring := editIP.text; SockAddr.sin_addr.S_addr := inet_addr(PAnsiChar(IPstring)); {нужен реальный адрес сетевой карты, иначе будет ошибка}
//Строчки от Валеры: RcvBufLen:= 82000000; //больше не должно придти setsockopt(MySocket,SOL_SOCKET,SO_RCVBUF,@RcvBufLen,4); // Для совместимости со старыми версиями Delphi приводим // константу INADDR_NONE к типу u_long if SockAddr.sin_addr.S_addr = u_long(INADDR_NONE) then begin MessageDlg('Неправильно задан IP адрес сокета программы', mtError, [mbOK], 0); Exit; end; SockAddr.sin_port := htons(3320); FillChar(SockAddr.sin_zero, SizeOf(SockAddr.sin_zero), 0); err := bind(MySocket, SockAddr, SizeOf(SockAddr)); if err = SOCKET_ERROR then MemoLog.Lines.Add('Ошибка привязки сокета программы: '+GetErrorString) else begin MemoLog.Lines.Add('Сокет программы привязан. Адрес '+ IntToStr(integer(SockAddr.sin_addr.S_un_b.s_b1))+'.'+ IntToStr(integer(SockAddr.sin_addr.S_un_b.s_b2))+'.'+ IntToStr(integer(SockAddr.sin_addr.S_un_b.s_b3))+'.'+ IntToStr(integer(SockAddr.sin_addr.S_un_b.s_b4))+', порт '+IntToStr(ntohs(SockAddr.sin_port))) end; //Заполнение структуры, описывающей сокет нашего устройства DeviceSock.sin_family := PF_INET; DeviceSock.sin_addr.S_addr := inet_addr('192.168.1.150'); if DeviceSock.sin_addr.S_addr = u_long(INADDR_NONE) then begin MessageDlg('Неправильно задан IP адрес сокета устройства', mtError, [mbOK], 0); Exit; end; DeviceSock.sin_port := htons(7); FillChar(DeviceSock.sin_zero, SizeOf(DeviceSock.sin_zero), 0); MemoLog.Lines.Add('Сокет устройства описан. Адрес '+ IntToStr(integer(DeviceSock.sin_addr.S_un_b.s_b1))+'.'+ IntToStr(integer(DeviceSock.sin_addr.S_un_b.s_b2))+'.'+ IntToStr(integer(DeviceSock.sin_addr.S_un_b.s_b3))+'.'+ IntToStr(integer(DeviceSock.sin_addr.S_un_b.s_b4))+', порт '+IntToStr(ntohs(DeviceSock.sin_port)));
//И функция приёма пакета: function TForm1.DataFromDevice: boolean; var // Множество сокетов для функции select. // Будет содержать только один сокет FSocket. SocketSet: TFDSet; // Таймаут для функции select Timeout: TTimeVal; begin // Инициализируем множество сокетов, // т.е. очищаем его от случайного мусора FD_ZERO(SocketSet); // Добавляем в это множество сокет FSocket FD_SET(MySocket, SocketSet); // Устанавливаем таймаут равным нулю, чтобы // функция select ничего не ждала, а возвращала // готовность сокетов на момент вызова. Timeout.tv_sec := 0; Timeout.tv_usec := 0; // Проверяем готовность сокета для чтения if select(0, @SocketSet, nil, nil, @Timeout) = SOCKET_ERROR then begin MemoLog.Lines.Add('Ошибка при проверке готовности сокета: ' + GetErrorString); Result := false; Exit; end; // Проверяем, оставила ли функция select сокет в множестве. // Если оставила, значит, во входном буфере сокета есть данные. if FD_ISSET(MySocket, SocketSet) then begin AddrLen := SizeOf(RecvAddr); // Получаем дейтаграмму RecvLen := recvfrom(MySocket, Buffer, SizeOf(Buffer), 0, RecvAddr, AddrLen); // Так как UDP не поддерживает соединение, ошибку при вызове recvfrom мы // можем получить, только если случилось что-то совсем экстраординарное. if RecvLen < 0 then begin MemoLog.Lines.Add('Ошибка при получении сообщения: ' + GetErrorString); Result := false; Exit end; Result := true end //копирования данных в приёмный буфер else begin Result := false end end; //TForm1.DataFromDevice
--------------------
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|