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 licence
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 //event sent by workers to server class
66 //after client is served
67 const wxEventType wxEVT_WORKER
= wxNewEventType();
68 #define EVT_WORKER(func) DECLARE_EVENT_TABLE_ENTRY( wxEVT_WORKER, -1, -1, (wxObjectEventFunction) (wxEventFunction) (WorkerEventFunction) & func, (wxObject *) NULL ),
70 class WorkerEvent
: public wxEvent
73 WorkerEvent(void* pSender
)
76 SetEventType(wxEVT_WORKER
);
79 m_workerFailed
= false;
82 virtual wxEvent
* Clone() const
84 return new WorkerEvent(*this);
92 typedef void (wxEvtHandler::*WorkerEventFunction
)(WorkerEvent
&);
97 WX_DECLARE_LIST(ThreadWorker
, TList
);
98 WX_DECLARE_LIST(EventWorker
, EList
);
100 //main server class contains listening socket
101 //and list of two type worker classes that serve clients
102 class Server
: public wxApp
104 DECLARE_EVENT_TABLE()
106 Server() : m_maxConnections(-1) {}
116 virtual bool OnInit();
117 virtual int OnExit();
119 void OnInitCmdLine(wxCmdLineParser
& pParser
);
120 bool OnCmdLineParsed(wxCmdLineParser
& pParser
);
122 void OnSocketEvent(wxSocketEvent
& pEvent
);
123 void OnWorkerEvent(WorkerEvent
& pEvent
);
124 void OnTimerEvent(wxTimerEvent
& pEvent
);
125 void DumpStatistics();
127 TList m_threadWorkers
;
128 EList m_eventWorkers
;
130 wxSocketServer
* m_listeningSocket
;
133 unsigned m_threadWorkersCreated
;
134 unsigned m_threadWorkersDone
;
135 unsigned m_threadWorkersFailed
;
136 unsigned m_maxThreadWorkers
;
138 unsigned m_eventWorkersCreated
;
139 unsigned m_eventWorkersDone
;
140 unsigned m_eventWorkersFailed
;
141 unsigned m_maxEventWorkers
;
143 long int m_maxConnections
;
145 unsigned short m_port
;
152 // just some common things shared between ThreadWorker and EventWorker
156 // outputs log message with IP and TCP port number prepended
157 void LogWorker(const wxString
& msg
, wxLogLevel level
= wxLOG_Info
)
160 "%s:%d %s", m_peer
.IPAddress(), m_peer
.Service(), msg
);
163 wxIPV4address m_peer
;
166 //thread based worker reads signature and all data first from connected client
167 //and resends data to client after reading
168 class ThreadWorker
: public wxThread
, private WorkerBase
171 ThreadWorker(wxSocketBase
* pSocket
);
172 virtual ExitCode
Entry();
175 wxSocketBase
* m_socket
;
178 //event based worker reads signature and creates buffer for incoming data.
179 //When part of data arrives this worker resends it as soon as possible.
180 class EventWorker
: public wxEvtHandler
, private WorkerBase
183 EventWorker(wxSocketBase
* pSock
);
184 virtual ~EventWorker();
187 wxSocketBase
* m_socket
;
189 unsigned char m_signature
[2];
197 void OnSocketEvent(wxSocketEvent
& pEvent
);
201 DECLARE_EVENT_TABLE()
204 /******************* Implementation ******************/
205 IMPLEMENT_APP_CONSOLE(Server
)
207 #include <wx/listimpl.cpp>
208 WX_DEFINE_LIST(TList
);
209 WX_DEFINE_LIST(EList
);
213 Server::OnInitCmdLine(wxCmdLineParser
& pParser
)
215 wxApp::OnInitCmdLine(pParser
);
216 pParser
.AddSwitch("t","threads","Use thread based workers only");
217 pParser
.AddSwitch("e","events","Use event based workers only");
218 pParser
.AddOption("m","max","Exit after <n> connections",
219 wxCMD_LINE_VAL_NUMBER
);
220 pParser
.AddOption("p","port","listen on given port (default 3000)",
221 wxCMD_LINE_VAL_NUMBER
);
225 Server::DumpStatistics()
231 mode
= "Event based workers";
234 mode
= "Thread based workers";
237 mode
= "Event and thread based workers";
240 wxLogMessage("Server mode: %s",mode
);
241 wxLogMessage("\t\t\t\tThreads\tEvents\tTotal");
242 wxLogMessage("Workers created:\t\t%d\t%d\t%d",
243 m_threadWorkersCreated
,
244 m_eventWorkersCreated
,
245 m_threadWorkersCreated
+ m_eventWorkersCreated
);
246 wxLogMessage("Max concurrent workers:\t%d\t%d\t%d",
249 m_maxThreadWorkers
+ m_maxEventWorkers
);
250 wxLogMessage("Workers failed:\t\t%d\t%d\t%d",
251 m_threadWorkersFailed
,
252 m_eventWorkersFailed
,
253 m_threadWorkersFailed
+ m_eventWorkersFailed
);
254 wxLogMessage("Workers done:\t\t%d\t%d\t%d",
257 m_threadWorkersDone
+ m_eventWorkersDone
);
259 if ((int)(m_threadWorkersDone
+m_eventWorkersDone
) == m_maxConnections
)
261 wxLogMessage("%ld connection(s) served, exiting",m_maxConnections
);
268 Server::OnCmdLineParsed(wxCmdLineParser
& pParser
)
270 if (pParser
.Found("verbose"))
272 wxLog::AddTraceMask("wxSocket");
273 wxLog::AddTraceMask("epolldispatcher");
274 wxLog::AddTraceMask("selectdispatcher");
275 wxLog::AddTraceMask("thread");
276 wxLog::AddTraceMask("events");
277 wxLog::AddTraceMask("timer");
280 if (pParser
.Found("m",&m_maxConnections
))
282 wxLogMessage("%ld connection(s) to exit",m_maxConnections
);
286 if (pParser
.Found("p", &port
))
288 if ( port
<= 0 || port
> USHRT_MAX
)
290 wxLogError("Invalid port number %ld, must be in 0..%u range.",
295 m_port
= static_cast<unsigned short>(port
);
296 wxLogMessage("Will listen on port %u", m_port
);
299 if (pParser
.Found("t"))
300 m_workMode
= THREADS
;
301 else if (pParser
.Found("e"))
306 return wxApp::OnCmdLineParsed(pParser
);
309 bool Server::OnInit()
311 wxLog
* logger
= new wxLogStderr();
312 wxLog::SetActiveTarget(logger
);
316 //send interesting things to console
317 if (!wxApp::OnInit())
320 //setup listening socket
323 m_listeningSocket
= new wxSocketServer(la
,wxSOCKET_NOWAIT
|wxSOCKET_REUSEADDR
);
324 m_listeningSocket
->SetEventHandler(*this);
325 m_listeningSocket
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
326 m_listeningSocket
->Notify(true);
327 if (!m_listeningSocket
->IsOk())
329 wxLogError("Cannot bind listening socket");
333 m_threadWorkersCreated
= 0;
334 m_threadWorkersDone
= 0;
335 m_threadWorkersFailed
= 0;
336 m_maxThreadWorkers
= 0;
338 m_eventWorkersCreated
= 0;
339 m_eventWorkersDone
= 0;
340 m_eventWorkersFailed
= 0;
341 m_maxEventWorkers
= 0;
343 wxLogMessage("Server listening at port %u, waiting for connections", m_port
);
350 for ( TList::compatibility_iterator it
= m_threadWorkers
.GetFirst();
354 it
->GetData()->Wait();
355 delete it
->GetData();
358 for ( EList::compatibility_iterator it2
= m_eventWorkers
.GetFirst();
362 delete it2
->GetData();
365 m_threadWorkers
.Clear();
366 m_eventWorkers
.Clear();
367 m_listeningSocket
->Destroy();
371 void Server::OnSocketEvent(wxSocketEvent
& pEvent
)
373 switch(pEvent
.GetSocketEvent())
376 wxLogError("Unexpected wxSOCKET_INPUT in wxSocketServer");
378 case wxSOCKET_OUTPUT
:
379 wxLogError("Unexpected wxSOCKET_OUTPUT in wxSocketServer");
381 case wxSOCKET_CONNECTION
:
383 wxSocketBase
* sock
= m_listeningSocket
->Accept();
385 if (!sock
->GetPeer(addr
))
387 wxLogError("Server: cannot get peer info");
389 wxLogMessage("Got connection from %s:%d",addr
.IPAddress().c_str(), addr
.Service());
393 if (m_workMode
!= MIXED
)
394 createThread
= m_workMode
== THREADS
;
396 createThread
= (wxDateTime::Now().GetSecond())%2
== 0;
400 ThreadWorker
* c
= new ThreadWorker(sock
);
401 if (c
->Create() == wxTHREAD_NO_ERROR
)
403 m_threadWorkers
.Append(c
);
404 if (m_threadWorkers
.GetCount() > m_maxThreadWorkers
)
405 m_maxThreadWorkers
++;
406 m_threadWorkersCreated
++;
411 wxLogError("Server: cannot create next thread (current threads: %d", m_threadWorkers
.size());
416 EventWorker
* w
= new EventWorker(sock
);
417 m_eventWorkers
.Append(w
);
418 if (m_eventWorkers
.GetCount() > m_maxEventWorkers
)
420 m_eventWorkersCreated
++;
425 wxLogError("Unexpected wxSOCKET_LOST in wxSocketServer");
430 void Server::OnWorkerEvent(WorkerEvent
& pEvent
)
432 //wxLogMessage("Got worker event");
433 for(TList::compatibility_iterator it
= m_threadWorkers
.GetFirst(); it
; it
= it
->GetNext())
435 if (it
->GetData() == pEvent
.m_sender
)
437 wxLogVerbose("Deleting thread worker (%lu left)",
438 static_cast<unsigned long>( m_threadWorkers
.GetCount() ));
439 it
->GetData()->Wait();
440 delete it
->GetData();
441 m_threadWorkers
.DeleteNode(it
);
442 if (!pEvent
.m_workerFailed
)
443 m_threadWorkersDone
++;
445 m_threadWorkersFailed
++;
449 for(EList::compatibility_iterator it2
= m_eventWorkers
.GetFirst(); it2
; it2
= it2
->GetNext())
451 if (it2
->GetData() == pEvent
.m_sender
)
453 wxLogVerbose("Deleting event worker (%lu left)",
454 static_cast<unsigned long>( m_eventWorkers
.GetCount() ));
455 delete it2
->GetData();
456 m_eventWorkers
.DeleteNode(it2
);
457 if (!pEvent
.m_workerFailed
)
458 m_eventWorkersDone
++;
460 m_eventWorkersFailed
++;
465 if (m_eventWorkers
.GetCount() == 0 && m_threadWorkers
.GetCount() == 0)
467 mTimer
.Start(1000,true);
471 void Server::OnTimerEvent(wxTimerEvent
&)
477 BEGIN_EVENT_TABLE(Server
,wxEvtHandler
)
478 EVT_SOCKET(wxID_ANY
,Server::OnSocketEvent
)
479 EVT_WORKER(Server::OnWorkerEvent
)
480 EVT_TIMER(wxID_ANY
,Server::OnTimerEvent
)
484 ThreadWorker::ThreadWorker(wxSocketBase
* pSocket
) : wxThread(wxTHREAD_JOINABLE
)
487 //Notify() cannot be called in thread context. We have to detach from main loop
488 //before switching thread contexts.
489 m_socket
->Notify(false);
490 m_socket
->SetFlags(wxSOCKET_WAITALL
|wxSOCKET_BLOCK
);
491 pSocket
->GetPeer(m_peer
);
494 wxThread::ExitCode
ThreadWorker::Entry()
497 if (!m_socket
->IsConnected())
499 LogWorker("ThreadWorker: not connected",wxLOG_Error
);
503 if (m_socket
->IsConnected())
505 unsigned char signature
[2];
506 LogWorker("ThreadWorker: reading for data");
510 m_socket
->Read(&signature
,to_process
);
511 if (m_socket
->Error())
513 LogWorker("ThreadWorker: Read error",wxLOG_Error
);
514 wxGetApp().AddPendingEvent(e
);
517 to_process
-= m_socket
->LastCount();
518 LogWorker(wxString::Format("to_process: %d",to_process
));
521 while (!m_socket
->Error() && to_process
!= 0);
523 if (signature
[0] == 0)
529 if (signature
[0] == 0xCE)
531 LogWorker("This server does not support test2 from GUI client",wxLOG_Error
);
532 e
.m_workerFailed
= true;
536 int size
= signature
[1] * (signature
[0] == 0xBE ? 1 : 1024);
537 char* buf
= new char[size
];
538 LogWorker(wxString::Format("Message signature: chunks: %d, kilobytes: %d, size: %d (bytes)",signature
[0],signature
[1],size
));
541 LogWorker(wxString::Format("ThreadWorker: reading %d bytes of data",to_process
));
545 m_socket
->Read(buf
,to_process
);
546 if (m_socket
->Error())
548 LogWorker("ThreadWorker: Read error",wxLOG_Error
);
549 wxGetApp().AddPendingEvent(e
);
552 to_process
-= m_socket
->LastCount();
553 LogWorker(wxString::Format("ThreadWorker: %d bytes readed, %d todo",m_socket
->LastCount(),to_process
));
556 while(!m_socket
->Error() && to_process
!= 0);
562 m_socket
->Write(buf
,to_process
);
563 if (m_socket
->Error())
565 LogWorker("ThreadWorker: Write error",wxLOG_Error
);
568 to_process
-= m_socket
->LastCount();
569 LogWorker(wxString::Format("ThreadWorker: %d bytes written, %d todo",m_socket
->LastCount(),to_process
));
571 while(!m_socket
->Error() && to_process
!= 0);
574 LogWorker("ThreadWorker: done");
575 e
.m_workerFailed
= to_process
!= 0;
577 wxGetApp().AddPendingEvent(e
);
581 EventWorker::EventWorker(wxSocketBase
* pSock
)
588 m_socket
->SetNotify(wxSOCKET_LOST_FLAG
|wxSOCKET_INPUT_FLAG
|wxSOCKET_OUTPUT_FLAG
);
589 m_socket
->Notify(true);
590 m_socket
->SetEventHandler(*this);
591 m_socket
->SetFlags(wxSOCKET_NOWAIT
);
592 m_socket
->GetPeer(m_peer
);
595 EventWorker::~EventWorker()
603 EventWorker::DoRead()
607 //read message header
610 m_socket
->Read(m_signature
+ m_infill
, 2 - m_infill
);
611 if (m_socket
->Error())
613 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
615 LogWorker(wxString::Format("Read error (%d): %s",m_socket
->LastError(),GetSocketErrorMsg(m_socket
->LastError())),wxLOG_Error
);
621 m_infill
+= m_socket
->LastCount();
624 unsigned char chunks
= m_signature
[1];
625 unsigned char type
= m_signature
[0];
628 LogWorker("This server does not support test2 from GUI client",wxLOG_Error
);
629 m_written
= -1; //wxSOCKET_LOST will interpret this as failure
632 else if (type
== 0xBE || type
== 0xDE)
634 m_size
= chunks
* (type
== 0xBE ? 1 : 1024);
635 m_inbuf
= new char[m_size
];
636 m_outbuf
= new char[m_size
];
640 LogWorker(wxString::Format("Message signature: len: %d, type: %s, size: %d (bytes)",chunks
,type
== 0xBE ? "b" : "kB",m_size
));
645 LogWorker(wxString::Format("Unknown test type %x",type
));
651 while(!m_socket
->Error() && (2 - m_infill
!= 0));
659 if (m_size
== m_infill
)
661 m_signature
[0] = m_signature
[1] = 0x0;
666 m_socket
->Read(m_inbuf
+ m_infill
,m_size
- m_infill
);
667 if (m_socket
->Error())
669 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
671 LogWorker(wxString::Format("Read error (%d): %s",
672 m_socket
->LastError(),
673 GetSocketErrorMsg(m_socket
->LastError())),
681 memcpy(m_outbuf
+m_outfill
,m_inbuf
+m_infill
,m_socket
->LastCount());
682 m_infill
+= m_socket
->LastCount();
683 m_outfill
+= m_socket
->LastCount();
687 while(!m_socket
->Error());
690 void EventWorker::OnSocketEvent(wxSocketEvent
& pEvent
)
692 switch(pEvent
.GetSocketEvent())
698 case wxSOCKET_OUTPUT
:
703 case wxSOCKET_CONNECTION
:
704 LogWorker("Unexpected wxSOCKET_CONNECTION in EventWorker", wxLOG_Error
);
709 LogWorker("Connection lost");
711 e
.m_workerFailed
= m_written
!= m_size
;
712 wxGetApp().AddPendingEvent(e
);
718 void EventWorker::DoWrite()
722 if (m_written
== m_size
)
726 LogWorker( "All data written");
729 if (m_outfill
- m_written
== 0)
733 m_socket
->Write(m_outbuf
+ m_written
,m_outfill
- m_written
);
734 if (m_socket
->Error())
736 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
739 wxString::Format("Write error (%d): %s",
740 m_socket
->LastError(),
741 GetSocketErrorMsg(m_socket
->LastError())
749 LogWorker("Write would block, waiting for OUTPUT event");
754 memmove(m_outbuf
,m_outbuf
+m_socket
->LastCount(),m_outfill
-m_socket
->LastCount());
755 m_written
+= m_socket
->LastCount();
757 LogWorker(wxString::Format("Written %d of %d bytes, todo %d",
758 m_socket
->LastCount(),m_size
,m_size
- m_written
));
760 while (!m_socket
->Error());
763 BEGIN_EVENT_TABLE(EventWorker
,wxEvtHandler
)
764 EVT_SOCKET(wxID_ANY
,EventWorker::OnSocketEvent
)