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
;
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("%d 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("%d connection(s) to exit",m_maxConnections
);
285 if (pParser
.Found("p",&m_port
))
287 wxLogMessage("%d connection(s) to exit",m_maxConnections
);
290 if (pParser
.Found("t"))
291 m_workMode
= THREADS
;
292 else if (pParser
.Found("e"))
297 return wxApp::OnCmdLineParsed(pParser
);
300 bool Server::OnInit()
302 wxLog
* logger
= new wxLogStderr();
303 wxLog::SetActiveTarget(logger
);
307 //send interesting things to console
308 if (!wxApp::OnInit())
311 //setup listening socket
314 m_listeningSocket
= new wxSocketServer(la
,wxSOCKET_NOWAIT
|wxSOCKET_REUSEADDR
);
315 m_listeningSocket
->SetEventHandler(*this);
316 m_listeningSocket
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
317 m_listeningSocket
->Notify(true);
318 if (!m_listeningSocket
->IsOk())
320 wxLogError("Cannot bind listening socket");
324 m_threadWorkersCreated
= 0;
325 m_threadWorkersDone
= 0;
326 m_threadWorkersFailed
= 0;
327 m_maxThreadWorkers
= 0;
329 m_eventWorkersCreated
= 0;
330 m_eventWorkersDone
= 0;
331 m_eventWorkersFailed
= 0;
332 m_maxEventWorkers
= 0;
334 wxLogMessage("Server listening at port %d, waiting for connections", m_port
);
341 for ( TList::compatibility_iterator it
= m_threadWorkers
.GetFirst();
345 it
->GetData()->Wait();
346 delete it
->GetData();
349 for ( EList::compatibility_iterator it2
= m_eventWorkers
.GetFirst();
353 delete it2
->GetData();
356 m_threadWorkers
.Clear();
357 m_eventWorkers
.Clear();
358 m_listeningSocket
->Destroy();
362 void Server::OnSocketEvent(wxSocketEvent
& pEvent
)
364 switch(pEvent
.GetSocketEvent())
367 wxLogError("Unexpected wxSOCKET_INPUT in wxSocketServer");
369 case wxSOCKET_OUTPUT
:
370 wxLogError("Unexpected wxSOCKET_OUTPUT in wxSocketServer");
372 case wxSOCKET_CONNECTION
:
374 wxSocketBase
* sock
= m_listeningSocket
->Accept();
376 if (!sock
->GetPeer(addr
))
378 wxLogError("Server: cannot get peer info");
380 wxLogMessage("Got connection from %s:%d",addr
.IPAddress().c_str(), addr
.Service());
384 if (m_workMode
!= MIXED
)
385 createThread
= m_workMode
== THREADS
;
387 createThread
= (wxDateTime::Now().GetSecond())%2
== 0;
391 ThreadWorker
* c
= new ThreadWorker(sock
);
392 if (c
->Create() == wxTHREAD_NO_ERROR
)
394 m_threadWorkers
.Append(c
);
395 if (m_threadWorkers
.GetCount() > m_maxThreadWorkers
)
396 m_maxThreadWorkers
++;
397 m_threadWorkersCreated
++;
402 wxLogError("Server: cannot create next thread (current threads: %d", m_threadWorkers
.size());
407 EventWorker
* w
= new EventWorker(sock
);
408 m_eventWorkers
.Append(w
);
409 if (m_eventWorkers
.GetCount() > m_maxEventWorkers
)
411 m_eventWorkersCreated
++;
416 wxLogError("Unexpected wxSOCKET_LOST in wxSocketServer");
421 void Server::OnWorkerEvent(WorkerEvent
& pEvent
)
423 //wxLogMessage("Got worker event");
424 for(TList::compatibility_iterator it
= m_threadWorkers
.GetFirst(); it
; it
= it
->GetNext())
426 if (it
->GetData() == pEvent
.m_sender
)
428 wxLogVerbose("Deleting thread worker (%d left)",
429 m_threadWorkers
.GetCount());
430 it
->GetData()->Wait();
431 delete it
->GetData();
432 m_threadWorkers
.DeleteNode(it
);
433 if (!pEvent
.m_workerFailed
)
434 m_threadWorkersDone
++;
436 m_threadWorkersFailed
++;
440 for(EList::compatibility_iterator it2
= m_eventWorkers
.GetFirst(); it2
; it2
= it2
->GetNext())
442 if (it2
->GetData() == pEvent
.m_sender
)
444 wxLogVerbose("Deleting event worker (%d left)",
445 m_eventWorkers
.GetCount());
446 delete it2
->GetData();
447 m_eventWorkers
.DeleteNode(it2
);
448 if (!pEvent
.m_workerFailed
)
449 m_eventWorkersDone
++;
451 m_eventWorkersFailed
++;
456 if (m_eventWorkers
.GetCount() == 0 && m_threadWorkers
.GetCount() == 0)
458 mTimer
.Start(1000,true);
462 void Server::OnTimerEvent(wxTimerEvent
&)
468 BEGIN_EVENT_TABLE(Server
,wxEvtHandler
)
469 EVT_SOCKET(wxID_ANY
,Server::OnSocketEvent
)
470 EVT_WORKER(Server::OnWorkerEvent
)
471 EVT_TIMER(wxID_ANY
,Server::OnTimerEvent
)
475 ThreadWorker::ThreadWorker(wxSocketBase
* pSocket
) : wxThread(wxTHREAD_JOINABLE
)
478 //Notify() cannot be called in thread context. We have to detach from main loop
479 //before switching thread contexts.
480 m_socket
->Notify(false);
481 m_socket
->SetFlags(wxSOCKET_WAITALL
|wxSOCKET_BLOCK
);
482 pSocket
->GetPeer(m_peer
);
485 wxThread::ExitCode
ThreadWorker::Entry()
488 if (!m_socket
->IsConnected())
490 LogWorker("ThreadWorker: not connected",wxLOG_Error
);
494 if (m_socket
->IsConnected())
496 unsigned char signature
[2];
497 LogWorker("ThreadWorker: reading for data");
501 m_socket
->Read(&signature
,to_process
);
502 if (m_socket
->Error())
504 LogWorker("ThreadWorker: Read error",wxLOG_Error
);
505 wxGetApp().AddPendingEvent(e
);
508 to_process
-= m_socket
->LastCount();
509 LogWorker(wxString::Format("to_process: %d",to_process
));
512 while (!m_socket
->Error() && to_process
!= 0);
514 if (signature
[0] == 0)
520 if (signature
[0] == 0xCE)
522 LogWorker("This server does not support test2 from GUI client",wxLOG_Error
);
523 e
.m_workerFailed
= true;
527 int size
= signature
[1] * (signature
[0] == 0xBE ? 1 : 1024);
528 char* buf
= new char[size
];
529 LogWorker(wxString::Format("Message signature: chunks: %d, kilobytes: %d, size: %d (bytes)",signature
[0],signature
[1],size
));
532 LogWorker(wxString::Format("ThreadWorker: reading %d bytes of data",to_process
));
536 m_socket
->Read(buf
,to_process
);
537 if (m_socket
->Error())
539 LogWorker("ThreadWorker: Read error",wxLOG_Error
);
540 wxGetApp().AddPendingEvent(e
);
543 to_process
-= m_socket
->LastCount();
544 LogWorker(wxString::Format("ThreadWorker: %d bytes readed, %d todo",m_socket
->LastCount(),to_process
));
547 while(!m_socket
->Error() && to_process
!= 0);
553 m_socket
->Write(buf
,to_process
);
554 if (m_socket
->Error())
556 LogWorker("ThreadWorker: Write error",wxLOG_Error
);
559 to_process
-= m_socket
->LastCount();
560 LogWorker(wxString::Format("ThreadWorker: %d bytes written, %d todo",m_socket
->LastCount(),to_process
));
562 while(!m_socket
->Error() && to_process
!= 0);
565 LogWorker("ThreadWorker: done");
566 e
.m_workerFailed
= to_process
!= 0;
568 wxGetApp().AddPendingEvent(e
);
572 EventWorker::EventWorker(wxSocketBase
* pSock
)
579 m_socket
->SetNotify(wxSOCKET_LOST_FLAG
|wxSOCKET_INPUT_FLAG
|wxSOCKET_OUTPUT_FLAG
);
580 m_socket
->Notify(true);
581 m_socket
->SetEventHandler(*this);
582 m_socket
->SetFlags(wxSOCKET_NOWAIT
);
583 m_socket
->GetPeer(m_peer
);
586 EventWorker::~EventWorker()
594 EventWorker::DoRead()
598 //read message header
601 m_socket
->Read(m_signature
+ m_infill
, 2 - m_infill
);
602 if (m_socket
->Error())
604 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
606 LogWorker(wxString::Format("Read error (%d): %s",m_socket
->LastError(),GetSocketErrorMsg(m_socket
->LastError())),wxLOG_Error
);
612 m_infill
+= m_socket
->LastCount();
615 unsigned char chunks
= m_signature
[1];
616 unsigned char type
= m_signature
[0];
619 LogWorker("This server does not support test2 from GUI client",wxLOG_Error
);
620 m_written
= -1; //wxSOCKET_LOST will interpret this as failure
623 else if (type
== 0xBE || type
== 0xDE)
625 m_size
= chunks
* (type
== 0xBE ? 1 : 1024);
626 m_inbuf
= new char[m_size
];
627 m_outbuf
= new char[m_size
];
631 LogWorker(wxString::Format("Message signature: len: %d, type: %s, size: %d (bytes)",chunks
,type
== 0xBE ? "b" : "kB",m_size
));
636 LogWorker(wxString::Format("Unknown test type %x",type
));
642 while(!m_socket
->Error() && (2 - m_infill
!= 0));
650 if (m_size
== m_infill
)
652 m_signature
[0] = m_signature
[1] = 0x0;
657 m_socket
->Read(m_inbuf
+ m_infill
,m_size
- m_infill
);
658 if (m_socket
->Error())
660 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
662 LogWorker(wxString::Format("Read error (%d): %s",
663 m_socket
->LastError(),
664 GetSocketErrorMsg(m_socket
->LastError())),
672 memcpy(m_outbuf
+m_outfill
,m_inbuf
+m_infill
,m_socket
->LastCount());
673 m_infill
+= m_socket
->LastCount();
674 m_outfill
+= m_socket
->LastCount();
678 while(!m_socket
->Error());
681 void EventWorker::OnSocketEvent(wxSocketEvent
& pEvent
)
683 switch(pEvent
.GetSocketEvent())
689 case wxSOCKET_OUTPUT
:
694 case wxSOCKET_CONNECTION
:
695 LogWorker("Unexpected wxSOCKET_CONNECTION in EventWorker", wxLOG_Error
);
700 LogWorker("Connection lost");
702 e
.m_workerFailed
= m_written
!= m_size
;
703 wxGetApp().AddPendingEvent(e
);
709 void EventWorker::DoWrite()
713 if (m_written
== m_size
)
717 LogWorker( "All data written");
720 if (m_outfill
- m_written
== 0)
724 m_socket
->Write(m_outbuf
+ m_written
,m_outfill
- m_written
);
725 if (m_socket
->Error())
727 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
730 wxString::Format("Write error (%d): %s",
731 m_socket
->LastError(),
732 GetSocketErrorMsg(m_socket
->LastError())
740 LogWorker("Write would block, waiting for OUTPUT event");
745 memmove(m_outbuf
,m_outbuf
+m_socket
->LastCount(),m_outfill
-m_socket
->LastCount());
746 m_written
+= m_socket
->LastCount();
748 LogWorker(wxString::Format("Written %d of %d bytes, todo %d",
749 m_socket
->LastCount(),m_size
,m_size
- m_written
));
751 while (!m_socket
->Error());
754 BEGIN_EVENT_TABLE(EventWorker
,wxEvtHandler
)
755 EVT_SOCKET(wxID_ANY
,EventWorker::OnSocketEvent
)