Очень быстрый прием UDP пакетов

cptngrb
Дата: 13.12.2019 15:57:33
Тестил на своей виртуальной машине компоненты ICS, Indy и стандартные WinSock (Win7-64, rio.1, win64)
ICS и Indy плюс минус 2480 пакетов успевают поймать, через стандартный WinSock 4700 пакетов, а отправляю я 9000 пакетов
WireShark ловит все 9000.
Причем при получении пакета я ничего не делаю, кроме как увеличиваю кол-во принятых пакетов.

Какими средствами вы пользуетесь?
cptngrb
Дата: 13.12.2019 16:02:47
в Indy использую idUDPServer событие OnRead, в WinSock recv, в ICS взял демку с TWSocket
s62
Дата: 13.12.2019 16:40:46
cptngr,
какой размер пакета?
cptngrb
Дата: 13.12.2019 16:41:25
s62, 1500 байт +- летят
wadman
Дата: 13.12.2019 16:50:25
defecator на чем-то писал видеосервис, не жаловался. :) может отметится...
cptngrb
Дата: 13.12.2019 17:04:23
wadman, у него вроде на IOCP
Dimitry Sibiryakov
Дата: 13.12.2019 17:07:12
cptngrb
WireShark ловит все 9000.

А на какой стороне этот WireShark?

И ты бы это... код показал хотя бы для WinSock. А то окажется внезапно, что у тебя там
ProcessMessages() в цикле...

PS: И да, я никогда не использую UDP.
cptngrb
Дата: 13.12.2019 17:19:09
Dimitry Sibiryakov,

constructor TReceiverUDP.Create(const AAdrIP: ansistring; const APort: word; const ASocketBufferSize, ALogType: integer);

begin

  inherited Create(True);
  Priority:= tpHighest;
  FreeOnTerminate:= false;
  ServerAddr.sin_family := AF_INET;
  ServerAddr.sin_addr.S_addr := inet_addr(PansiChar(AAdrIP));
  ServerAddr.sin_port := htons(APort);
  FSocketBufferSize:= ASocketBufferSize;
  FLogType:= ALogType;
  //{$IFDEF DEBUG}
  FPacketCount:= 0;
  //{$ENDIF}
end;

procedure TReceiverUDP.DestroyUDP;
begin
  try
    CloseSocket(FSocket);
    WSACleanup;
  except
    on E: exception do
    begin
      WriteMessage(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) +' - (!ОШИБКА)  закрытие сокетов. ' + E.message);
    end;
  end;
end;

procedure TReceiverUDP.Execute;
begin
  if InitUDP then begin
    while not Terminated do
    begin
      ReceiverPackage;
    end;

    //{$IFDEF DEBUG}
    WriteMessage(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) + ' - (!ИНФ) : Кол-во принятых пакетов = '+inttostr(FPacketCount));
    //{$ENDIF}
  end;
  DestroyUDP;
end;

function TReceiverUDP.InitUDP: boolean;
var
  Len:Integer;
  errCode: integer;
  Arg: u_long;
begin
  Result:= False;
  try
    if WSAStartup($0101, wData) = 0 then begin
      FSocket := Socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if bind(FSocket, ServerAddr, SizeOf(ServerAddr)) = Socket_Error then
      begin
        errCode:= WSAGetLastError();
        WriteMessage(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) +  ' - (!ОШИБКА)  bind socket вернул ошибку #'+errCode.ToString);
      end
       else begin
        Len:= SizeOf(Integer);
        if SetSockOpt(FSocket, SOL_SOCKET, SO_RCVBUF, @FSocketBufferSize, Len) = Socket_Error then
        begin
          errCode:= WSAGetLastError();
          WriteMessage(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) +  ' - (!ОШИБКА)  Установка буфера приема сокета #'+errCode.ToString);
        end
        else begin
          {если вдург широковещательный канал
          EnBroad:=1;
          SetSockOpt(FSocket,SOL_Socket,SO_Broadcast,PChar(@EnBroad),SizeOf(Integer));}
          {  перевожу сокет в неблокирующий режим  }
          Arg:=1;
          if IOCtlSocket(FSocket, FIONBIO, Arg) = Socket_Error then
          begin
            errCode:= WSAGetLastError();
            WriteMessage(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) +  ' - (!ОШИБКА)  Установка неблокируемого режима работы сокета #'+errCode.ToString);
          end;
          //GetSockOpt(FSocket, SOL_SOCKET, SO_RCVBUF, vInt);
          Result:= True;
        end;
      end;
    end;
  except
    on E: exception do
    begin
      WriteMessage(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) +  ' - (!ОШИБКА)  инициализация модуля приема UDP не выполнена' + E.message);
    end;
  end;
end;

procedure TReceiverUDP.ReceiverPackage;
var
  sizePacket: LongInt;
  Buffer: array [0..DefBufferSize-1] of byte;
begin
  if SERVICE_STOP = 1 then exit;

  try
    sizePacket:= Recv(FSocket, Buffer, DefBufferSize, 0);
    if sizePacket > 0 then
    begin
      //{$IFDEF DEBUG}
      inc(FPacketCount);
      //{$ENDIF}


    end;
  except
    on E: exception do
    begin

    WriteLog(FormatDateTime('yyyy_mm_dd_hh:nn:ss', now) + ' - (!Ошибка) :' + E.message);
    end;
  end;

end;
_Vasilisk_
Дата: 13.12.2019 17:19:17
Вангую переменную длину пакета, без указания/считывания этой длины в протоколе
DmSer
Дата: 13.12.2019 17:22:42
cptngrb

ICS и Indy плюс минус 2480 пакетов успевают поймать


Indy наверняка доп. поток на каждый пакет заводят, а затем удаляют. А на это время требуется. Потоки создаются со скоростью примерно 10 тыс. в сек. С какой скоростью удаляются - не мерил.
Там вроде можно пул потоков использовать. Не пробовали?

Еще тормоза добавляет дельфёвый менеджер памяти - он не умеет паспараллеливаться по ядрам. Попробуйте потестировать на Лазарусе, тот умеет. Правда память будет ждать как паровоз. Но можно собрать из транка, там вроде проблему с пожиранием памяти устранили.
Собирать нужно как 64-битное приложение, иначе упрётесь в нехватку виртуальной памяти.