-
Здравствуйте все! Я соорудил некое устройство, которое обменивается данными с компьютером по UDP. На компьютере работает моя самодельная программа, написанная в Delphi 7. Она работает с UDP через WinAPI так, как написано в книге А.Б. Григорьева "О чём не пишут в книгах по Delphi", стр. 204 и далее. Или я думаю, что она так работает. Пока надо было гонять туда-сюда по два-три пакета, всё было хорошо. Сейчас я отправляю из устройства подряд 256 пакетов размером поменьше MTU с интервалом 4 мс. Все эти пакеты появляются в компьютере, их видно в сниффере WireShark, и содержимое у них правильное. Но моя программа, приняв 5 первых пакетов, затем примерно полсекунды не видит приходящих пакетов. А потом она нормально принимает все оставшиеся, больше сотни. Содержимое каждого пакета - байты с его номером, так что легко понять, чего не хватает, а что пришло. То есть, как я понимаю, системе хватает быстродействия, чтобы принимать пакеты каждые 4 мс. Причём даже по 2 штуки за 4 мс: я пробовал слать пакеты больше MTU, они разбивались на пары внутри сетевого интерфейса моего устройства и нормально доходили, и тогда до пропадания программа нормально принимала 9 пакетов. Как бороться с пропаданием пакетов после начала передачи? Прошу не бить сильно, в программировании я любитель. Что мне ещё надо написать, чтобы на мой вопрос надо было ответить?
-
> Что мне ещё надо написать, чтобы на мой вопрос надо было > ответить?
код приема наверное показать
-
Объявлены переменные: Buffer: array[0..65506] of Byte;
RecvAddr: TSockAddr;
RecvLen, AddrLen: Integer;
ErrorCount: integer;
SocketSet: TFDSet;
Timeout: TTimeVal; При запуске программы (onCreate формы): var
WSData: TWSAData;
err: integer;
SocketVal,SocketLen: integer; ..... //Инициализация библиотеки сокетов (стр. 204 у Григорьева): err := WSAStartup($0101,WSData);
if err = 0 then MemoLog.Lines.Add('Инициализация библиотеки сокетов: OK')
else
begin
err := WSAGetLastError;
MemoLog.Lines.Add('Ошибка инициализации библиотеки сокетов с кодом'+IntToStr(err))
end;
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;
SockAddr.sin_family := PF_INET;
IPstring := editIP.text;
SockAddr.sin_addr.S_addr := inet_addr(PAnsiChar(IPstring));
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)));
err := DataCalculation;
if err <> 0 then
memoLog.Lines.Add('Задан неправильный размер блока данных, код '+IntToStr(err));
FD_ZERO(SocketSet);
FD_SET(MySocket, SocketSet);
Timeout.tv_sec := 0;
Timeout.tv_usec := 0;
И функция чтения сокета: function TForm1.DataFromDevice: boolean;
begin
if select(0, @SocketSet, nil, nil, @Timeout) = SOCKET_ERROR then
begin
MemoLog.Lines.Add('Ошибка при проверке готовности сокета: ' + GetErrorString);
Result := false;
Exit;
end;
if FD_ISSET(MySocket, SocketSet) then
begin
AddrLen := SizeOf(RecvAddr);
RecvLen := recvfrom(MySocket, Buffer, SizeOf(Buffer), 0, RecvAddr, AddrLen);
if RecvLen < 0 then
begin
MemoLog.Lines.Add('Ошибка при получении сообщения: ' + GetErrorString);
Result := false;
Exit
end;
Result := true
end else
begin
Result := false
end
-
как и когда вызывается функция DataFromDevice?
-
Вызывается так: if DataFromDevice then - приняли данные else - повторяем вызов DataFromDevice, и так до истечения тайм-аута. По замыслу, так должны выгребаться все пришедшие пакеты. Вызывается она после того, как из компьютера уходят в устройство три пакета с управляющими данными, в которых, в частности, содержится команда для устройства передать данные в компьютер.
-
> Вызывается так: а правиться значится так. садишься значит, читаешь код и если видишь написана ошибка заменяешь ее на правильный... ну т.е. if DataFromDevice then - это ошибка! то пишешь if DataFromDevice_2 then - это правильно. после исправлений "билдиш" проект, это частная команда компилируящая все файлы, был ли для нее dcu или нет.
-
Прием данных ведется в основном потоке? Чем занимается программа, приняв первую порцию - может, какими-то относительно долгими действиями?
-
Да, в основном потоке. Программа пытается непрерывно выгребать приходящие пакеты, больше ничем не занимается.
-
т.е. организация такая - запустили, программа крутит цикл приема, в котором принимает пакет и записывает его номер, пока не придет весь набор пакетов? Ни перерисовкой формы, ни выводом контрольных данных в это время не занимается?
-
допрос партизана продолжается второй день... но не выдал он тайны партизанской.
-
to MBo: Да, всё почти так. Только после принятия пакета программа выводит в TMemo строчку с отчётом о приёме и номером вида: "Пакет № содержит N".
-
Попробуй не выводить сразу, а накопить всё в TStringList, например, и вывести по окончанию серии
-
А не хватало всего двух строчек: RcvBufLen:= 82000000; //больше не должно придти setsockopt(MySocket,SOL_SOCKET,SO_RCVBUF,@RcvBufLen,4); Профессиональный программист посидел пару часов с моим кодом. Эти строчки задают размер буфера, в который складываются поступающие данные. Итого, вот весь код для приёма данных по UDP с использованием только WinAPI. А то я уже с перепугу чуть не начал изучать WinPCAP.
var
MySocket: TSocket;
SockAddr, DeviceSock: TSockAddr;
Buffer: array[0..65506] of Byte;
RecvAddr: TSockAddr;
RecvLen, AddrLen: Integer;
err := WSAStartup($0101,WSData);
if err = 0 then MemoLog.Lines.Add('Инициализация библиотеки сокетов: OK')
else
begin
err := WSAGetLastError;
MemoLog.Lines.Add('Ошибка инициализации библиотеки сокетов с кодом'+IntToStr(err))
end;
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;
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);
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
SocketSet: TFDSet;
Timeout: TTimeVal;
begin
FD_ZERO(SocketSet);
FD_SET(MySocket, SocketSet);
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;
if FD_ISSET(MySocket, SocketSet) then
begin
AddrLen := SizeOf(RecvAddr);
RecvLen := recvfrom(MySocket, Buffer, SizeOf(Buffer), 0, RecvAddr, AddrLen);
if RecvLen < 0 then
begin
MemoLog.Lines.Add('Ошибка при получении сообщения: ' + GetErrorString);
Result := false;
Exit
end;
Result := true
end else
begin
Result := false
end
end;
-
> [12]
а как связано отсутствие этих строчек с пропаданием пакетов?
-
как пользуешься DataFromDevice? кто ее выполняет? мож она на кнопку прицеплена :)
|