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