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