Пакеты правильные, потому что их формирую не я, а умная штуковина 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