Hi,
I've a problem with udp.
Often my application detects packet losses.

In order to systematically reproduce the problem, I've used the
following configuration.
I use 2 pc connected with a cross cable.
The client sends udp numbered packets to the server.
Packet size is 1460. One packet is sent every 1 ms (about 11 Mbps).

I use wireshark to analyze the traffic on the server machine.
I see that all the packets arrive on the server, but not inside the
application.

Packet loss increases if I resize the form, so I've made a worker thread
in order to manage udp server socket messages, but the problem remains.

Have you any suggestion to resolve this problem?
Maybe the socket configuration should be changed?

Thank you for your help,
Emanuele

I attach the code of server, client and worker thread:

************************************************************************************
Server code
************************************************************************************
unit Main;

interface

uses
  ewPlatform_WorkerThread,
  OverbyteIcsWSocket,
  WinSock,
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TOnListen=procedure(aSender:TObject;aThreadID:Cardinal)of object;
  TOnData=procedure(aSender:TObject;aRx,aSize:integer)of object;
  TUDPServer=class(TWorkerThread)
  protected
    fExpected:integer;
    fExpectedSize:integer;
    fWS:TWSocket;
    procedure DoInit;override;
    procedure WSocketSessionConnected(Sender: TObject; Error: Word);
    procedure WSocketSessionClosed(Sender: TObject; Error: Word);
    procedure WSocketDataAvailable(Sender: TObject; Error: Word);
    procedure WndProc(var aMessage:TMessage);override;
    procedure ThreadWndProc(var aMessage:TMessage);override;
  public
    pBR:integer;
    pOnListen:TOnListen;
    pOnClose:TNotifyEvent;
    pOnData:TOnData;
    pOnSizeError:TOnData;

    destructor Destroy;override;
    procedure Listen(aIP:string;aPort:string;aExpectedSize:integer);
    procedure Close;

  end;

  TForm1 = class(TForm)
    ButtonStart: TButton;
    ButtonStop: TButton;
    Memo1: TMemo;
    EditIP: TEdit;
    EditPort: TEdit;
    Timer1: TTimer;
    LabelBR: TLabel;
    Button1: TButton;
    EditExpectedSize: TEdit;
    procedure ButtonStartClick(Sender: TObject);
    procedure ButtonStopClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    fUDPServer:TUDPServer;
    procedure OnListen(aSender:TObject;aThreadID:cardinal);
    procedure OnClose(aSender:TObject);
    procedure OnData(aSender:TObject;aRx,aExpected:integer);
    procedure OnSizeError(aSender:TObject;aRx,aExpected:integer);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

  WM_UDP_LISTEN:cardinal;
  WM_UDP_CLOSE:cardinal;
  WM_UDP_DATA:cardinal;
  WM_UDP_SIZE:cardinal;

implementation

{$R *.dfm}

//------------------------------------------------------------------------------
//TUDPServer
//------------------------------------------------------------------------------
procedure TUDPServer.DoInit;
begin
  pBR:=0;
  fExpected:=0;
  fExpectedSize:=0;
  fWS:=TWSocket.Create(nil);
  fWS.OnSessionConnected:=WSocketSessionConnected;
  fWS.OnSessionClosed:=WSocketSessionClosed;
  fWS.OnDataAvailable:=WSocketDataAvailable;
end;

destructor TUDPServer.Destroy;
begin
  inherited Destroy;
  FreeAndNil(fWS);
end;

procedure TUDPServer.Listen(aIP,aPort:string;aExpectedSize:integer);
begin
  fExpectedSize:=aExpectedSize;
  fWS.Proto:='udp';
  fWS.MultiThreaded:=true;
  fWS.Addr:=aIP;
  fWS.Port:=aPort;
  PostMessage(self.fThreadHandle,WM_UDP_LISTEN,0,0);
end;

procedure TUDPServer.Close;
begin
  PostMessage(self.fThreadHandle,WM_UDP_CLOSE,0,0);
end;

procedure TUDPServer.WSocketSessionConnected(Sender: TObject;Error: Word);
begin
  PostMessage(self.fHandle,WM_UDP_LISTEN,GetCurrentThreadID,0);
end;

procedure TUDPServer.WSocketSessionClosed(Sender: TObject; Error: Word);
begin
  PostMessage(self.fHandle,WM_UDP_CLOSE,0,0);
end;

procedure TUDPServer.WSocketDataAvailable(Sender: TObject; Error: Word);
var
  lBuffer:array[0..1500] of AnsiChar;
  lLen:integer;
  lSrc:TSockAddrIn;
  lSrcLen:integer;
  lRx:integer;
begin
  lSrcLen:=SizeOf(lSrc);
  lLen:=fWS.ReceiveFrom(@lBuffer,SizeOf(lBuffer),lSrc,lSrcLen);
  if lLen>=0 then
  begin
    inc(pBR,lLen);
    move(lBuffer[0],lRx,4);
    if lRx<> fExpected then
      PostMessage(self.fHandle,WM_UDP_DATA,lRx,fExpected);
    fExpected:=lRx+1;
    if lLen<>fExpectedSize then
      PostMessage(self.fHandle,WM_UDP_SIZE,lLen,fExpectedSize);
  end;
end;

procedure TUDPServer.WndProc(var aMessage:TMessage);
begin
  if aMessage.Msg=WM_UDP_LISTEN then
  begin
    if assigned(pOnListen) then
      pOnListen(self,aMessage.WParam);
  end;
  if aMessage.Msg=WM_UDP_CLOSE then
  begin
    if assigned(pOnClose) then
      pOnClose(self);
  end;
  if aMessage.Msg=WM_UDP_DATA then
  begin
    if assigned(pOnData) then
      pOnData(self,aMessage.WParam,aMessage.LParam);
  end;
  if aMessage.Msg=WM_UDP_SIZE then
  begin
    if assigned(pOnSizeError) then
      pOnSizeError(self,aMessage.WParam,aMEssage.LParam);
  end;
  inherited WndProc(aMessage);
end;

procedure TUDPServer.ThreadWndProc(var aMessage:TMessage);
begin
 if aMessage.Msg=WM_UDP_LISTEN then
  begin
    fExpected:=0;
    fWS.Listen;
  end;
  if aMessage.Msg=WM_UDP_CLOSE then
  begin
    fWS.Close;
  end;
  inherited ThreadWndProc(aMessage);
end;

//------------------------------------------------------------------------------
//Main
//------------------------------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
  OnClose(nil);
  fUDPServer:=TUDPServer.Create;
  fUDPServer.pOnListen:=OnListen;
  fUDPServer.pOnClose:=OnClose;
  fUDPServer.pOnData:=OnData;
  fUDPServer.pOnSizeError:=OnSizeError;
  //fUDPServer.Priority:=tpTimeCritical;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(fUDPServer);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  LabelBR.Caption:='BR:'+inttostr((fUDPServer.pBR*8)div 1000)+'kbps';
  fUDPServer.pBR:=0;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
end;

procedure TForm1.ButtonStartClick(Sender: TObject);
begin
fUDPServer.Listen(EditIP.Text,EditPort.Text,strtointdef(EditExpectedSize.Text,1000));
end;

procedure TForm1.ButtonStopClick(Sender: TObject);
begin
  fUDPServer.Close;
end;

procedure TForm1.OnListen(aSender:TObject;aThreadID:cardinal);
begin
  ButtonStart.Enabled:=false;
  ButtonStop.Enabled:=true;
  Caption:='Listening';
  Memo1.Lines.Add('Main thread ID '+inttostr(GetCurrentThreadID)+' - UDP
thread ID '+inttostr(aThreadID));
end;

procedure TForm1.OnClose(aSender:TObject);
begin
  ButtonStart.Enabled:=true;
  ButtonStop.Enabled:=false;
  Caption:='Closed';
end;

procedure TForm1.OnData(aSender:TObject;aRx,aExpected:integer);
begin
  if aRx<>aExpected then
   Memo1.Lines.Add('received '+inttostr(aRx)+' - expected
'+inttostr(aExpected));
end;

procedure TForm1.OnSizeError(aSender:TObject;aRx,aExpected:integer);
begin
  Memo1.Lines.Add('size '+inttostr(aRx)+' - expected size
'+inttostr(aExpected));
end;

initialization
  WM_UDP_LISTEN:=RegisterWindowMessage('WM_UDP_LISTEN');
  WM_UDP_CLOSE:=RegisterWindowMessage('WM_UDP_CLOSE');
  WM_UDP_DATA:=RegisterWindowMessage('WM_UDP_DATAAVAILABLE');
  WM_UDP_SIZE:=RegisterWindowMessage('WM_UDP_SIZEERROR');

end.

************************************************************************************
Client code
************************************************************************************
unit Main;

interface

uses
  ewPlatform_WorkerThread,
  OverbyteIcsWSocket,
  WinSock,
  MMSystem,
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TUDPTimer=class(TWorkerThread)
  protected
    procedure WndProc(var aMessage:TMessage);override;
    procedure ThreadWndProc(var aMessage:TMessage);override;
  public
    pOnTimer:TNotifyEvent;
    procedure Restart(aInterval:integer);
  end;

  TForm1 = class(TForm)
    EditIP: TEdit;
    EditPort: TEdit;
    EditDataSize: TEdit;
    EditInterval: TEdit;
    ButtonStop: TButton;
    ButtonStart: TButton;
    Timer1: TTimer;
    LabelBR: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ButtonStartClick(Sender: TObject);
    procedure ButtonStopClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
    fTimer:TUDPTimer;
    fBR:integer;
    fWS: TWSocket;
    fPeerSrc:TSockAddr;
    fPeerSrcLen:integer;
    fData:pointer;
    fDataSize:integer;
    fCounter:integer;
    procedure WSocketSessionConnected(Sender: TObject; Error: Word);
    procedure WSocketSessionClosed(Sender: TObject; Error: Word);
    procedure OnTimer(aSender:TObject);
    procedure Restart;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

  WM_UDP_TIMER:cardinal;

implementation

{$R *.dfm}

//------------------------------------------------------------------------------
//TUDPClient.
//------------------------------------------------------------------------------
procedure TUDPTimer.ThreadWndProc(var aMessage:TMessage);
begin
  if aMessage.Msg=WM_UDP_TIMER then
  begin
    sleep(aMessage.WParam);
    PostMessage(self.fHandle,WM_UDP_TIMER,0,0);
  end;
  inherited ThreadWndProc(aMessage);
end;

procedure TUDPTimer.WndProc(var aMessage:TMessage);
begin
  if aMessage.Msg=WM_UDP_TIMER then
  begin
    if assigned(pOnTimer) then
      pOnTimer(self);
  end;
  inherited WndProc(aMessage);
end;

procedure TUDPTimer.Restart(aInterval:integer);
begin
  if not self.Suspended then
    PostMessage(self.fThreadHandle,WM_UDP_TIMER,aInterval,0);
end;

//------------------------------------------------------------------------------
//Main
//------------------------------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
  fData:=nil;
  fPeerSrcLen:=sizeof(fPeerSrc);
  fTimer:=TUDPTimer.Create;
  fTimer.pOnTimer:=OnTimer;
  fWS:=TWSocket.Create(nil);
  fWS.OnSessionConnected:=WSocketSessionConnected;
  fWS.OnSessionClosed:=WSocketSessionClosed;
  WSocketSessionClosed(nil,0);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(fTimer);
  FreeAndNil(fWS);
  if assigned(fData) then
    FreeMem(fData);
end;

procedure TForm1.ButtonStartClick(Sender: TObject);
var
  lPeer:array[0..255] of char;
begin
  fCounter:=0;
  if assigned(fData) then
    FreeMem(fData);
  fDataSize:=strtointdef(EditDataSize.Text,1460);
  getmem(fData,fDataSize);
  fWS.Proto:= 'udp';
  fWS.Addr:= EditIP.Text;
  fWS.Port:= EditPort.Text;
  fPeerSrc.sin_family:=AF_INET;
  strpcopy(lPeer,fWS.Addr);
  fPeerSrc.sin_addr.s_addr:=WSocket_inet_addr(lPeer);
  fPeerSrc.sin_port:=htons(word(strtoint(fWS.Port)));

  fWS.Connect;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  LabelBR.Caption:='BR:'+inttostr((fBR*8)div 1000)+'kbps';
  fBR:=0;
end;

procedure TForm1.ButtonStopClick(Sender: TObject);
begin
  fWS.Close;
end;

procedure TForm1.WSocketSessionConnected(Sender: TObject;Error: Word);
begin
  ButtonStart.Enabled:=false;
  ButtonStop.Enabled:=true;
  Caption:='Connected';
  Restart;
end;

procedure TForm1.Restart;
begin
  fTimer.Restart(strtointdef(EditInterval.Text,10));
end;

procedure TForm1.WSocketSessionClosed(Sender: TObject; Error: Word);
begin
  ButtonStart.Enabled:=true;
  ButtonStop.Enabled:=false;
  Caption:='Disconnected';
end;

procedure TForm1.OnTimer(aSender:TObject);
begin
  if fWS.State=wsConnected then
  begin
    move(fCounter,fData^,4);
    if fWS.SendTo(fPeerSrc,fPeerSrcLen,fData,fDataSize)>0 then
    begin
      inc(fCounter);
      inc(fBR,fDataSize);
    end;
    Restart;
  end;
end;

initialization
  WM_UDP_TIMER:=RegisterWindowMessage('WM_UDP_TIMER');
  TimeBeginPeriod(1);
finalization
  TimeEndPeriod(1);

end.

************************************************************************************
Worker thread
************************************************************************************
unit ewPlatform_WorkerThread;

interface

uses
  Messages,
  Windows,
  Classes;

type
  TWorkerThread=class(TThread)
  protected
    fHandle:cardinal;
    fThreadHandle:cardinal;
    fResumeEvent:THandle;
    procedure DoInit;virtual;
    procedure MessageLoop;
    procedure WndProc(var aMessage:TMessage);virtual;
    procedure ThreadWndProc(var aMessage:TMessage);virtual;
  public
    constructor Create;
    destructor Destroy;override;
    procedure Execute;override;
  end;

implementation

//------------------------------------------------------------------------------
//TWorkerThread.
//------------------------------------------------------------------------------
constructor TWorkerThread.Create;
begin
  inherited Create(true);
  fHandle:=AllocateHWnd(WndProc);
  fResumeEvent:=CreateEvent(nil,false,false,nil);
  self.Resume;
  WaitForSingleObject(fResumeEvent,INFINITE);
end;

destructor TWorkerThread.Destroy;
begin
  if (not self.Terminated)and(not self.Suspended) then
  begin
    self.Terminate;
    PostThreadMessage(self.ThreadID,WM_QUIT,0,0);
    self.WaitFor;
  end;
  DeallocateHWnd(fHandle);
  CloseHandle(fResumeEvent);
  inherited Destroy;
end;

procedure TWorkerThread.ThreadWndProc(var aMessage:TMessage);
begin
aMessage.Result:=DefWindowProc(fThreadHandle,aMessage.Msg,aMessage.wParam,aMessage.lParam);
end;

procedure TWorkerThread.WndProc(var aMessage:TMessage);
begin
aMessage.Result:=DefWindowProc(fHandle,aMessage.Msg,aMessage.wParam,aMessage.lParam);
end;

procedure TWorkerThread.DoInit;
begin
end;

procedure TWorkerThread.Execute;
begin
  DoInit;
  MessageLoop;
end;

procedure TWorkerThread.MessageLoop;
var
  lMsg:TMsg;
begin
  fThreadHandle:=AllocateHWnd(ThreadWndProc);
  PeekMessage(lMsg,0,0,0,PM_NOREMOVE);
  SetEvent(fResumeEvent);
  //GetMessage return false on WM_QUIT
  while (GetMessage(lMsg,0,0,0))and(not self.Terminated) do
  begin
    TranslateMessage(lMsg);
    DispatchMessage(lMsg);
  end;
  DeallocateHWnd(fThreadHandle);
end;

end.
--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be

Reply via email to