1 /////////////////////////////////////////////////////////////////////////////
2 // Name: samples/sockbase/client.cpp
3 // Purpose: Sockets sample for wxBase
4 // Author: Lukasz Michalski
8 // Copyright: (c) 2005 Lukasz Michalski <lmichalski@user.sourceforge.net>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #include "wx/socket.h"
24 #include "wx/cmdline.h"
25 #include "wx/datetime.h"
27 #include "wx/thread.h"
29 const char *GetSocketErrorMsg(int pSockError
)
33 case wxSOCKET_NOERROR
:
34 return "wxSOCKET_NOERROR";
37 return "wxSOCKET_INVOP";
40 return "wxSOCKET_IOERR";
42 case wxSOCKET_INVADDR
:
43 return "wxSOCKET_INVADDR";
46 return "wxSOCKET_NOHOST";
48 case wxSOCKET_INVPORT
:
49 return "wxSOCKET_INVPORT";
51 case wxSOCKET_WOULDBLOCK
:
52 return "wxSOCKET_WOULDBLOCK";
54 case wxSOCKET_TIMEDOUT
:
55 return "wxSOCKET_TIMEDOUT";
58 return "wxSOCKET_MEMERR";
65 // outputs log message with IP and TCP port number prepended
67 LogWorker(const wxIPV4address
& addr
,
69 wxLogLevel level
= wxLOG_Info
)
71 wxLogGeneric(level
, "%s:%d %s", addr
.IPAddress(), addr
.Service(), msg
);
74 //event sent by workers to server class
75 //after client is served
76 const wxEventType wxEVT_WORKER
= wxNewEventType();
77 #define EVT_WORKER(func) DECLARE_EVENT_TABLE_ENTRY( wxEVT_WORKER, -1, -1, (wxObjectEventFunction) (wxEventFunction) (WorkerEventFunction) & func, (wxObject *) NULL ),
79 class WorkerEvent
: public wxEvent
82 WorkerEvent(void* pSender
)
85 SetEventType(wxEVT_WORKER
);
88 m_workerFailed
= false;
91 virtual wxEvent
* Clone() const
93 return new WorkerEvent(*this);
101 typedef void (wxEvtHandler
::*WorkerEventFunction
)(WorkerEvent
&);
106 WX_DECLARE_LIST(ThreadWorker
, TList
);
107 WX_DECLARE_LIST(EventWorker
, EList
);
109 //main server class contains listening socket
110 //and list of two type worker classes that serve clients
111 class Server
: public wxApp
113 DECLARE_EVENT_TABLE();
115 Server() : m_maxConnections(-1) {}
125 virtual bool OnInit();
126 virtual int OnExit();
128 void OnInitCmdLine(wxCmdLineParser
& pParser
);
129 bool OnCmdLineParsed(wxCmdLineParser
& pParser
);
131 void OnSocketEvent(wxSocketEvent
& pEvent
);
132 void OnWorkerEvent(WorkerEvent
& pEvent
);
133 void OnTimerEvent(wxTimerEvent
& pEvent
);
134 void DumpStatistics();
136 TList m_threadWorkers
;
137 EList m_eventWorkers
;
139 wxSocketServer
* m_listeningSocket
;
142 unsigned m_threadWorkersCreated
;
143 unsigned m_threadWorkersDone
;
144 unsigned m_threadWorkersFailed
;
145 unsigned m_maxThreadWorkers
;
147 unsigned m_eventWorkersCreated
;
148 unsigned m_eventWorkersDone
;
149 unsigned m_eventWorkersFailed
;
150 unsigned m_maxEventWorkers
;
152 long int m_maxConnections
;
161 //thread based worker reads signature and all data first from connected client
162 //and resends data to client after reading
163 class ThreadWorker
: public wxThread
166 ThreadWorker(wxSocketBase
* pSocket
);
167 virtual ExitCode
Entry();
169 wxSocketBase
* m_socket
;
170 wxIPV4address m_peer
;
173 //event based worker reads signature and creates buffer for incoming data.
174 //When part of data arrives this worker resends it as soon as possible.
175 class EventWorker
: public wxEvtHandler
177 DECLARE_EVENT_TABLE();
179 EventWorker(wxSocketBase
* pSock
);
182 wxSocketBase
* m_socket
;
183 wxIPV4address m_peer
;
185 unsigned char m_signature
[2];
193 void OnSocketEvent(wxSocketEvent
& pEvent
);
198 /******************* Implementation ******************/
199 IMPLEMENT_APP_CONSOLE(Server
)
201 #include <wx/listimpl.cpp>
202 WX_DEFINE_LIST(TList
);
203 WX_DEFINE_LIST(EList
);
207 Server
::OnInitCmdLine(wxCmdLineParser
& pParser
)
209 wxApp
::OnInitCmdLine(pParser
);
210 pParser
.AddSwitch("t","threads",_("Use thread based workers only"));
211 pParser
.AddSwitch("e","events",_("Use event based workers only"));
212 pParser
.AddOption("m","max",_("Exit after <n> connections"),wxCMD_LINE_VAL_NUMBER
,wxCMD_LINE_PARAM_OPTIONAL
);
213 pParser
.AddOption("p","port",_("listen on given port (default 3000)"),wxCMD_LINE_VAL_NUMBER
,wxCMD_LINE_PARAM_OPTIONAL
);
217 Server
::DumpStatistics()
223 mode
= _("Event based workers");
226 mode
= _("Thread based workers");
229 mode
= _("Event and thread based workers");
232 wxLogMessage("Server mode: %s",mode
);
233 wxLogMessage("\t\t\t\tThreads\tEvents\tTotal");
234 wxLogMessage("Workers created:\t\t%d\t%d\t%d",
235 m_threadWorkersCreated
,
236 m_eventWorkersCreated
,
237 m_threadWorkersCreated
+ m_eventWorkersCreated
);
238 wxLogMessage("Max concurrent workers:\t%d\t%d\t%d",
241 m_maxThreadWorkers
+ m_maxEventWorkers
);
242 wxLogMessage("Workers failed:\t\t%d\t%d\t%d",
243 m_threadWorkersFailed
,
244 m_eventWorkersFailed
,
245 m_threadWorkersFailed
+ m_eventWorkersFailed
);
246 wxLogMessage("Workers done:\t\t%d\t%d\t%d",
249 m_threadWorkersDone
+ m_eventWorkersDone
);
251 if ((int)(m_threadWorkersDone
+m_eventWorkersDone
) == m_maxConnections
)
253 wxLogMessage("%d connection(s) served, exiting",m_maxConnections
);
260 Server
::OnCmdLineParsed(wxCmdLineParser
& pParser
)
262 if (pParser
.Found(_("verbose")))
264 wxLog
::AddTraceMask("wxSocket");
265 wxLog
::AddTraceMask("epolldispatcher");
266 wxLog
::AddTraceMask("selectdispatcher");
267 wxLog
::AddTraceMask("thread");
268 wxLog
::AddTraceMask("events");
269 wxLog
::AddTraceMask("timer");
272 if (pParser
.Found("m",&m_maxConnections
))
274 wxLogMessage("%d connection(s) to exit",m_maxConnections
);
277 if (pParser
.Found("p",&m_port
))
279 wxLogMessage("%d connection(s) to exit",m_maxConnections
);
282 if (pParser
.Found("t"))
283 m_workMode
= THREADS
;
284 else if (pParser
.Found("e"))
289 return wxApp
::OnCmdLineParsed(pParser
);
292 bool Server
::OnInit()
294 wxLog
* logger
= new wxLogStderr();
295 wxLog
::SetActiveTarget(logger
);
299 //send interesting things to console
300 if (!wxApp
::OnInit())
303 //setup listening socket
306 m_listeningSocket
= new wxSocketServer(la
,wxSOCKET_NOWAIT
|wxSOCKET_REUSEADDR
);
307 m_listeningSocket
->SetEventHandler(*this);
308 m_listeningSocket
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
309 m_listeningSocket
->Notify(true);
310 if (!m_listeningSocket
->Ok())
312 wxLogError("Cannot bind listening socket");
316 m_threadWorkersCreated
= 0;
317 m_threadWorkersDone
= 0;
318 m_threadWorkersFailed
= 0;
319 m_maxThreadWorkers
= 0;
321 m_eventWorkersCreated
= 0;
322 m_eventWorkersDone
= 0;
323 m_eventWorkersFailed
= 0;
324 m_maxEventWorkers
= 0;
326 wxLogMessage("Server listening at port %d, waiting for connections", m_port
);
333 for ( TList
::compatibility_iterator it
= m_threadWorkers
.GetFirst();
337 it
->GetData()->Wait();
338 delete it
->GetData();
341 for ( EList
::compatibility_iterator it2
= m_eventWorkers
.GetFirst();
345 delete it2
->GetData();
348 m_threadWorkers
.Clear();
349 m_eventWorkers
.Clear();
350 m_listeningSocket
->Destroy();
354 void Server
::OnSocketEvent(wxSocketEvent
& pEvent
)
356 switch(pEvent
.GetSocketEvent())
359 wxLogError("Unexpected wxSOCKET_INPUT in wxSocketServer");
361 case wxSOCKET_OUTPUT
:
362 wxLogError("Unexpected wxSOCKET_OUTPUT in wxSocketServer");
364 case wxSOCKET_CONNECTION
:
366 wxSocketBase
* sock
= m_listeningSocket
->Accept();
368 if (!sock
->GetPeer(addr
))
370 wxLogError("Server: cannot get peer info");
372 wxLogMessage("Got connection from %s:%d",addr
.IPAddress().c_str(), addr
.Service());
376 if (m_workMode
!= MIXED
)
377 createThread
= m_workMode
== THREADS
;
379 createThread
= (wxDateTime
::Now().GetSecond())%2 == 0;
383 ThreadWorker
* c
= new ThreadWorker(sock
);
384 if (c
->Create() == wxTHREAD_NO_ERROR
)
386 m_threadWorkers
.Append(c
);
387 if (m_threadWorkers
.GetCount() > m_maxThreadWorkers
)
388 m_maxThreadWorkers
++;
389 m_threadWorkersCreated
++;
394 wxLogError("Server: cannot create next thread (current threads: %d", m_threadWorkers
.size());
399 EventWorker
* w
= new EventWorker(sock
);
400 m_eventWorkers
.Append(w
);
401 if (m_eventWorkers
.GetCount() > m_maxEventWorkers
)
403 m_eventWorkersCreated
++;
408 wxLogError("Unexpected wxSOCKET_LOST in wxSocketServer");
413 void Server
::OnWorkerEvent(WorkerEvent
& pEvent
)
415 //wxLogMessage("Got worker event");
416 for(TList
::compatibility_iterator it
= m_threadWorkers
.GetFirst(); it
; it
= it
->GetNext())
418 if (it
->GetData() == pEvent
.m_sender
)
420 wxLogVerbose("Deleting thread worker (%d left)",
421 m_threadWorkers
.GetCount());
422 it
->GetData()->Wait();
423 delete it
->GetData();
424 m_threadWorkers
.DeleteNode(it
);
425 if (!pEvent
.m_workerFailed
)
426 m_threadWorkersDone
++;
428 m_threadWorkersFailed
++;
432 for(EList
::compatibility_iterator it2
= m_eventWorkers
.GetFirst(); it2
; it2
= it2
->GetNext())
434 if (it2
->GetData() == pEvent
.m_sender
)
436 wxLogVerbose("Deleting event worker (%d left)",
437 m_eventWorkers
.GetCount());
438 delete it2
->GetData();
439 m_eventWorkers
.DeleteNode(it2
);
440 if (!pEvent
.m_workerFailed
)
441 m_eventWorkersDone
++;
443 m_eventWorkersFailed
++;
448 if (m_eventWorkers
.GetCount() == 0 && m_threadWorkers
.GetCount() == 0)
450 mTimer
.Start(1000,true);
454 void Server
::OnTimerEvent(wxTimerEvent
&)
460 BEGIN_EVENT_TABLE(Server
,wxEvtHandler
)
461 EVT_SOCKET(wxID_ANY
,Server
::OnSocketEvent
)
462 EVT_WORKER(Server
::OnWorkerEvent
)
463 EVT_TIMER(wxID_ANY
,Server
::OnTimerEvent
)
467 ThreadWorker
::ThreadWorker(wxSocketBase
* pSocket
) : wxThread(wxTHREAD_JOINABLE
)
470 //Notify() cannot be called in thread context. We have to detach from main loop
471 //before switching thread contexts.
472 m_socket
->Notify(false);
473 m_socket
->SetFlags(wxSOCKET_WAITALL
|wxSOCKET_BLOCK
);
474 pSocket
->GetPeer(m_peer
);
477 wxThread
::ExitCode ThreadWorker
::Entry()
480 if (!m_socket
->IsConnected())
482 LogWorker(m_peer
,"ThreadWorker: not connected",wxLOG_Error
);
486 if (m_socket
->IsConnected())
488 unsigned char signature
[2];
489 LogWorker(m_peer
,"ThreadWorker: reading for data");
493 m_socket
->Read(&signature
,to_process
);
494 if (m_socket
->Error())
496 LogWorker(m_peer
,"ThreadWorker: Read error",wxLOG_Error
);
497 wxGetApp().AddPendingEvent(e
);
500 to_process
-= m_socket
->LastCount();
501 LogWorker(m_peer
,wxString
::Format("to_process: %d",to_process
));
504 while (!m_socket
->Error() && to_process
!= 0);
506 if (signature
[0] == 0)
512 if (signature
[0] == 0xCE)
514 LogWorker(m_peer
,_("This server does not support test2 from GUI client"),wxLOG_Error
);
515 e
.m_workerFailed
= true;
519 int size
= signature
[1] * (signature
[0] == 0xBE ?
1 : 1024);
520 char* buf
= new char[size
];
521 LogWorker(m_peer
,wxString
::Format("Message signature: chunks: %d, kilobytes: %d, size: %d (bytes)",signature
[0],signature
[1],size
));
524 LogWorker(m_peer
,wxString
::Format("ThreadWorker: reading %d bytes of data",to_process
));
528 m_socket
->Read(buf
,to_process
);
529 if (m_socket
->Error())
531 LogWorker(m_peer
,"ThreadWorker: Read error",wxLOG_Error
);
532 wxGetApp().AddPendingEvent(e
);
535 to_process
-= m_socket
->LastCount();
536 LogWorker(m_peer
,wxString
::Format("ThreadWorker: %d bytes readed, %d todo",m_socket
->LastCount(),to_process
));
539 while(!m_socket
->Error() && to_process
!= 0);
545 m_socket
->Write(buf
,to_process
);
546 if (m_socket
->Error())
548 LogWorker(m_peer
,"ThreadWorker: Write error",wxLOG_Error
);
551 to_process
-= m_socket
->LastCount();
552 LogWorker(m_peer
,wxString
::Format("ThreadWorker: %d bytes written, %d todo",m_socket
->LastCount(),to_process
));
554 while(!m_socket
->Error() && to_process
!= 0);
557 LogWorker(m_peer
,"ThreadWorker: done");
558 e
.m_workerFailed
= to_process
!= 0;
560 wxGetApp().AddPendingEvent(e
);
564 EventWorker
::EventWorker(wxSocketBase
* pSock
)
571 m_socket
->SetNotify(wxSOCKET_LOST_FLAG
|wxSOCKET_INPUT_FLAG
|wxSOCKET_OUTPUT_FLAG
);
572 m_socket
->Notify(true);
573 m_socket
->SetEventHandler(*this);
574 m_socket
->SetFlags(wxSOCKET_NOWAIT
);
575 m_socket
->GetPeer(m_peer
);
578 EventWorker
::~EventWorker()
586 EventWorker
::DoRead()
590 //read message header
593 m_socket
->Read(m_signature
,2 - m_infill
);
594 if (m_socket
->Error())
596 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
598 LogWorker(m_peer
,wxString
::Format("Read error (%d): %s",m_socket
->LastError(),GetSocketErrorMsg(m_socket
->LastError())),wxLOG_Error
);
604 m_infill
+= m_socket
->LastCount();
607 unsigned char chunks
= m_signature
[1];
608 unsigned char type
= m_signature
[0];
611 LogWorker(m_peer
,_("This server does not support test2 from GUI client"),wxLOG_Error
);
612 m_written
= -1; //wxSOCKET_LOST will interpret this as failure
615 else if (type
== 0xBE || type
== 0xDE)
617 m_size
= chunks
* (type
== 0xBE ?
1 : 1024);
618 m_inbuf
= new char[m_size
];
619 m_outbuf
= new char[m_size
];
623 LogWorker(m_peer
,wxString
::Format("Message signature: len: %d, type: %s, size: %d (bytes)",chunks
,type
== 0xBE ?
"b" : "kB",m_size
));
627 LogWorker(m_peer
,wxString
::Format("Unknown test type %x",type
));
633 while(!m_socket
->Error() && (2 - m_infill
!= 0));
641 if (m_size
== m_infill
)
643 m_signature
[0] = m_signature
[1] = 0x0;
649 m_socket
->Read(m_inbuf
+ m_infill
,m_size
- m_infill
);
650 if (m_socket
->Error())
652 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
656 wxString
::Format("Read error (%d): %s",
657 m_socket
->LastError(),
658 GetSocketErrorMsg(m_socket
->LastError())
667 memcpy(m_outbuf
+m_outfill
,m_inbuf
+m_infill
,m_socket
->LastCount());
668 m_infill
+= m_socket
->LastCount();
669 m_outfill
+= m_socket
->LastCount();
673 while(!m_socket
->Error());
676 void EventWorker
::OnSocketEvent(wxSocketEvent
& pEvent
)
678 switch(pEvent
.GetSocketEvent())
684 case wxSOCKET_OUTPUT
:
689 case wxSOCKET_CONNECTION
:
690 LogWorker(m_peer
,wxString
::Format("Unexpected wxSOCKET_CONNECTION in EventWorker"),wxLOG_Error
);
695 LogWorker(m_peer
,wxString
::Format("Connection lost"));
697 e
.m_workerFailed
= m_written
!= m_size
;
698 wxGetApp().AddPendingEvent(e
);
704 void EventWorker
::DoWrite()
708 if (m_written
== m_size
)
713 LogWorker(m_peer
, "All data written");
716 if (m_outfill
- m_written
== 0)
720 m_socket
->Write(m_outbuf
+ m_written
,m_outfill
- m_written
);
721 if (m_socket
->Error())
723 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
726 wxString
::Format("Write error (%d): %s",
727 m_socket
->LastError(),
728 GetSocketErrorMsg(m_socket
->LastError())
736 LogWorker(m_peer
,"Write would block, waiting for OUTPUT event");
741 memmove(m_outbuf
,m_outbuf
+m_socket
->LastCount(),m_outfill
-m_socket
->LastCount());
742 m_written
+= m_socket
->LastCount();
744 LogWorker(m_peer
,wxString
::Format("Written %d of %d bytes, todo %d",m_socket
->LastCount(),m_size
,m_size
- m_written
));
746 while (!m_socket
->Error());
749 BEGIN_EVENT_TABLE(EventWorker
,wxEvtHandler
)
750 EVT_SOCKET(wxID_ANY
,EventWorker
::OnSocketEvent
)