1 /////////////////////////////////////////////////////////////////////////////
2 // Name: samples/sockbase/client.cpp
3 // Purpose: Sockets sample for wxBase
4 // Author: Lukasz Michalski
7 // Copyright: (c) 2005 Lukasz Michalski <lmichalski@user.sourceforge.net>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
20 #include "wx/socket.h"
23 #include "wx/cmdline.h"
24 #include "wx/datetime.h"
26 #include "wx/thread.h"
28 const char *GetSocketErrorMsg(int pSockError
)
32 case wxSOCKET_NOERROR
:
33 return "wxSOCKET_NOERROR";
36 return "wxSOCKET_INVOP";
39 return "wxSOCKET_IOERR";
41 case wxSOCKET_INVADDR
:
42 return "wxSOCKET_INVADDR";
45 return "wxSOCKET_NOHOST";
47 case wxSOCKET_INVPORT
:
48 return "wxSOCKET_INVPORT";
50 case wxSOCKET_WOULDBLOCK
:
51 return "wxSOCKET_WOULDBLOCK";
53 case wxSOCKET_TIMEDOUT
:
54 return "wxSOCKET_TIMEDOUT";
57 return "wxSOCKET_MEMERR";
64 //event sent by workers to server class
65 //after client is served
66 const wxEventType wxEVT_WORKER
= wxNewEventType();
67 #define EVT_WORKER(func) DECLARE_EVENT_TABLE_ENTRY( wxEVT_WORKER, -1, -1, (wxObjectEventFunction) (wxEventFunction) (WorkerEventFunction) & func, (wxObject *) NULL ),
69 class WorkerEvent
: public wxEvent
72 WorkerEvent(void* pSender
)
75 SetEventType(wxEVT_WORKER
);
78 m_workerFailed
= false;
81 virtual wxEvent
* Clone() const
83 return new WorkerEvent(*this);
91 typedef void (wxEvtHandler::*WorkerEventFunction
)(WorkerEvent
&);
96 WX_DECLARE_LIST(ThreadWorker
, TList
);
97 WX_DECLARE_LIST(EventWorker
, EList
);
99 //main server class contains listening socket
100 //and list of two type worker classes that serve clients
101 class Server
: public wxApp
103 DECLARE_EVENT_TABLE()
105 Server() : m_maxConnections(-1) {}
115 virtual bool OnInit();
116 virtual int OnExit();
118 void OnInitCmdLine(wxCmdLineParser
& pParser
);
119 bool OnCmdLineParsed(wxCmdLineParser
& pParser
);
121 void OnSocketEvent(wxSocketEvent
& pEvent
);
122 void OnWorkerEvent(WorkerEvent
& pEvent
);
123 void OnTimerEvent(wxTimerEvent
& pEvent
);
124 void DumpStatistics();
126 TList m_threadWorkers
;
127 EList m_eventWorkers
;
129 wxSocketServer
* m_listeningSocket
;
132 unsigned m_threadWorkersCreated
;
133 unsigned m_threadWorkersDone
;
134 unsigned m_threadWorkersFailed
;
135 unsigned m_maxThreadWorkers
;
137 unsigned m_eventWorkersCreated
;
138 unsigned m_eventWorkersDone
;
139 unsigned m_eventWorkersFailed
;
140 unsigned m_maxEventWorkers
;
142 long int m_maxConnections
;
144 unsigned short m_port
;
151 // just some common things shared between ThreadWorker and EventWorker
155 // outputs log message with IP and TCP port number prepended
156 void LogWorker(const wxString
& msg
, wxLogLevel level
= wxLOG_Info
)
159 "%s:%d %s", m_peer
.IPAddress(), m_peer
.Service(), msg
);
162 wxIPV4address m_peer
;
165 //thread based worker reads signature and all data first from connected client
166 //and resends data to client after reading
167 class ThreadWorker
: public wxThread
, private WorkerBase
170 ThreadWorker(wxSocketBase
* pSocket
);
171 virtual ExitCode
Entry();
174 wxSocketBase
* m_socket
;
177 //event based worker reads signature and creates buffer for incoming data.
178 //When part of data arrives this worker resends it as soon as possible.
179 class EventWorker
: public wxEvtHandler
, private WorkerBase
182 EventWorker(wxSocketBase
* pSock
);
183 virtual ~EventWorker();
186 wxSocketBase
* m_socket
;
188 unsigned char m_signature
[2];
196 void OnSocketEvent(wxSocketEvent
& pEvent
);
200 DECLARE_EVENT_TABLE()
203 /******************* Implementation ******************/
204 IMPLEMENT_APP_CONSOLE(Server
)
206 #include <wx/listimpl.cpp>
207 WX_DEFINE_LIST(TList
);
208 WX_DEFINE_LIST(EList
);
212 Server::OnInitCmdLine(wxCmdLineParser
& pParser
)
214 wxApp::OnInitCmdLine(pParser
);
215 pParser
.AddSwitch("t","threads","Use thread based workers only");
216 pParser
.AddSwitch("e","events","Use event based workers only");
217 pParser
.AddOption("m","max","Exit after <n> connections",
218 wxCMD_LINE_VAL_NUMBER
);
219 pParser
.AddOption("p","port","listen on given port (default 3000)",
220 wxCMD_LINE_VAL_NUMBER
);
224 Server::DumpStatistics()
230 mode
= "Event based workers";
233 mode
= "Thread based workers";
236 mode
= "Event and thread based workers";
239 wxLogMessage("Server mode: %s",mode
);
240 wxLogMessage("\t\t\t\tThreads\tEvents\tTotal");
241 wxLogMessage("Workers created:\t\t%d\t%d\t%d",
242 m_threadWorkersCreated
,
243 m_eventWorkersCreated
,
244 m_threadWorkersCreated
+ m_eventWorkersCreated
);
245 wxLogMessage("Max concurrent workers:\t%d\t%d\t%d",
248 m_maxThreadWorkers
+ m_maxEventWorkers
);
249 wxLogMessage("Workers failed:\t\t%d\t%d\t%d",
250 m_threadWorkersFailed
,
251 m_eventWorkersFailed
,
252 m_threadWorkersFailed
+ m_eventWorkersFailed
);
253 wxLogMessage("Workers done:\t\t%d\t%d\t%d",
256 m_threadWorkersDone
+ m_eventWorkersDone
);
258 if ((int)(m_threadWorkersDone
+m_eventWorkersDone
) == m_maxConnections
)
260 wxLogMessage("%ld connection(s) served, exiting",m_maxConnections
);
267 Server::OnCmdLineParsed(wxCmdLineParser
& pParser
)
269 if (pParser
.Found("verbose"))
271 wxLog::AddTraceMask("wxSocket");
272 wxLog::AddTraceMask("epolldispatcher");
273 wxLog::AddTraceMask("selectdispatcher");
274 wxLog::AddTraceMask("thread");
275 wxLog::AddTraceMask("events");
276 wxLog::AddTraceMask("timer");
279 if (pParser
.Found("m",&m_maxConnections
))
281 wxLogMessage("%ld connection(s) to exit",m_maxConnections
);
285 if (pParser
.Found("p", &port
))
287 if ( port
<= 0 || port
> USHRT_MAX
)
289 wxLogError("Invalid port number %ld, must be in 0..%u range.",
294 m_port
= static_cast<unsigned short>(port
);
295 wxLogMessage("Will listen on port %u", m_port
);
298 if (pParser
.Found("t"))
299 m_workMode
= THREADS
;
300 else if (pParser
.Found("e"))
305 return wxApp::OnCmdLineParsed(pParser
);
308 bool Server::OnInit()
310 wxLog
* logger
= new wxLogStderr();
311 wxLog::SetActiveTarget(logger
);
315 //send interesting things to console
316 if (!wxApp::OnInit())
319 //setup listening socket
322 m_listeningSocket
= new wxSocketServer(la
,wxSOCKET_NOWAIT
|wxSOCKET_REUSEADDR
);
323 m_listeningSocket
->SetEventHandler(*this);
324 m_listeningSocket
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
325 m_listeningSocket
->Notify(true);
326 if (!m_listeningSocket
->IsOk())
328 wxLogError("Cannot bind listening socket");
332 m_threadWorkersCreated
= 0;
333 m_threadWorkersDone
= 0;
334 m_threadWorkersFailed
= 0;
335 m_maxThreadWorkers
= 0;
337 m_eventWorkersCreated
= 0;
338 m_eventWorkersDone
= 0;
339 m_eventWorkersFailed
= 0;
340 m_maxEventWorkers
= 0;
342 wxLogMessage("Server listening at port %u, waiting for connections", m_port
);
349 for ( TList::compatibility_iterator it
= m_threadWorkers
.GetFirst();
353 it
->GetData()->Wait();
354 delete it
->GetData();
357 for ( EList::compatibility_iterator it2
= m_eventWorkers
.GetFirst();
361 delete it2
->GetData();
364 m_threadWorkers
.Clear();
365 m_eventWorkers
.Clear();
366 m_listeningSocket
->Destroy();
370 void Server::OnSocketEvent(wxSocketEvent
& pEvent
)
372 switch(pEvent
.GetSocketEvent())
375 wxLogError("Unexpected wxSOCKET_INPUT in wxSocketServer");
377 case wxSOCKET_OUTPUT
:
378 wxLogError("Unexpected wxSOCKET_OUTPUT in wxSocketServer");
380 case wxSOCKET_CONNECTION
:
382 wxSocketBase
* sock
= m_listeningSocket
->Accept();
384 if (!sock
->GetPeer(addr
))
386 wxLogError("Server: cannot get peer info");
388 wxLogMessage("Got connection from %s:%d",addr
.IPAddress().c_str(), addr
.Service());
392 if (m_workMode
!= MIXED
)
393 createThread
= m_workMode
== THREADS
;
395 createThread
= (wxDateTime::Now().GetSecond())%2
== 0;
399 ThreadWorker
* c
= new ThreadWorker(sock
);
400 if (c
->Create() == wxTHREAD_NO_ERROR
)
402 m_threadWorkers
.Append(c
);
403 if (m_threadWorkers
.GetCount() > m_maxThreadWorkers
)
404 m_maxThreadWorkers
++;
405 m_threadWorkersCreated
++;
410 wxLogError("Server: cannot create next thread (current threads: %d", m_threadWorkers
.size());
415 EventWorker
* w
= new EventWorker(sock
);
416 m_eventWorkers
.Append(w
);
417 if (m_eventWorkers
.GetCount() > m_maxEventWorkers
)
419 m_eventWorkersCreated
++;
424 wxLogError("Unexpected wxSOCKET_LOST in wxSocketServer");
429 void Server::OnWorkerEvent(WorkerEvent
& pEvent
)
431 //wxLogMessage("Got worker event");
432 for(TList::compatibility_iterator it
= m_threadWorkers
.GetFirst(); it
; it
= it
->GetNext())
434 if (it
->GetData() == pEvent
.m_sender
)
436 wxLogVerbose("Deleting thread worker (%lu left)",
437 static_cast<unsigned long>( m_threadWorkers
.GetCount() ));
438 it
->GetData()->Wait();
439 delete it
->GetData();
440 m_threadWorkers
.DeleteNode(it
);
441 if (!pEvent
.m_workerFailed
)
442 m_threadWorkersDone
++;
444 m_threadWorkersFailed
++;
448 for(EList::compatibility_iterator it2
= m_eventWorkers
.GetFirst(); it2
; it2
= it2
->GetNext())
450 if (it2
->GetData() == pEvent
.m_sender
)
452 wxLogVerbose("Deleting event worker (%lu left)",
453 static_cast<unsigned long>( m_eventWorkers
.GetCount() ));
454 delete it2
->GetData();
455 m_eventWorkers
.DeleteNode(it2
);
456 if (!pEvent
.m_workerFailed
)
457 m_eventWorkersDone
++;
459 m_eventWorkersFailed
++;
464 if (m_eventWorkers
.GetCount() == 0 && m_threadWorkers
.GetCount() == 0)
466 mTimer
.Start(1000,true);
470 void Server::OnTimerEvent(wxTimerEvent
&)
476 BEGIN_EVENT_TABLE(Server
,wxEvtHandler
)
477 EVT_SOCKET(wxID_ANY
,Server::OnSocketEvent
)
478 EVT_WORKER(Server::OnWorkerEvent
)
479 EVT_TIMER(wxID_ANY
,Server::OnTimerEvent
)
483 ThreadWorker::ThreadWorker(wxSocketBase
* pSocket
) : wxThread(wxTHREAD_JOINABLE
)
486 //Notify() cannot be called in thread context. We have to detach from main loop
487 //before switching thread contexts.
488 m_socket
->Notify(false);
489 m_socket
->SetFlags(wxSOCKET_WAITALL
|wxSOCKET_BLOCK
);
490 pSocket
->GetPeer(m_peer
);
493 wxThread::ExitCode
ThreadWorker::Entry()
496 if (!m_socket
->IsConnected())
498 LogWorker("ThreadWorker: not connected",wxLOG_Error
);
502 if (m_socket
->IsConnected())
504 unsigned char signature
[2];
505 LogWorker("ThreadWorker: reading for data");
509 m_socket
->Read(&signature
,to_process
);
510 if (m_socket
->Error())
512 LogWorker("ThreadWorker: Read error",wxLOG_Error
);
513 wxGetApp().AddPendingEvent(e
);
516 to_process
-= m_socket
->LastCount();
517 LogWorker(wxString::Format("to_process: %d",to_process
));
520 while (!m_socket
->Error() && to_process
!= 0);
522 if (signature
[0] == 0)
528 if (signature
[0] == 0xCE)
530 LogWorker("This server does not support test2 from GUI client",wxLOG_Error
);
531 e
.m_workerFailed
= true;
535 int size
= signature
[1] * (signature
[0] == 0xBE ? 1 : 1024);
536 char* buf
= new char[size
];
537 LogWorker(wxString::Format("Message signature: chunks: %d, kilobytes: %d, size: %d (bytes)",signature
[0],signature
[1],size
));
540 LogWorker(wxString::Format("ThreadWorker: reading %d bytes of data",to_process
));
544 m_socket
->Read(buf
,to_process
);
545 if (m_socket
->Error())
547 LogWorker("ThreadWorker: Read error",wxLOG_Error
);
548 wxGetApp().AddPendingEvent(e
);
551 to_process
-= m_socket
->LastCount();
552 LogWorker(wxString::Format("ThreadWorker: %d bytes readed, %d todo",m_socket
->LastCount(),to_process
));
555 while(!m_socket
->Error() && to_process
!= 0);
561 m_socket
->Write(buf
,to_process
);
562 if (m_socket
->Error())
564 LogWorker("ThreadWorker: Write error",wxLOG_Error
);
567 to_process
-= m_socket
->LastCount();
568 LogWorker(wxString::Format("ThreadWorker: %d bytes written, %d todo",m_socket
->LastCount(),to_process
));
570 while(!m_socket
->Error() && to_process
!= 0);
573 LogWorker("ThreadWorker: done");
574 e
.m_workerFailed
= to_process
!= 0;
576 wxGetApp().AddPendingEvent(e
);
580 EventWorker::EventWorker(wxSocketBase
* pSock
)
587 m_socket
->SetNotify(wxSOCKET_LOST_FLAG
|wxSOCKET_INPUT_FLAG
|wxSOCKET_OUTPUT_FLAG
);
588 m_socket
->Notify(true);
589 m_socket
->SetEventHandler(*this);
590 m_socket
->SetFlags(wxSOCKET_NOWAIT
);
591 m_socket
->GetPeer(m_peer
);
594 EventWorker::~EventWorker()
602 EventWorker::DoRead()
606 //read message header
609 m_socket
->Read(m_signature
+ m_infill
, 2 - m_infill
);
610 if (m_socket
->Error())
612 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
614 LogWorker(wxString::Format("Read error (%d): %s",m_socket
->LastError(),GetSocketErrorMsg(m_socket
->LastError())),wxLOG_Error
);
620 m_infill
+= m_socket
->LastCount();
623 unsigned char chunks
= m_signature
[1];
624 unsigned char type
= m_signature
[0];
627 LogWorker("This server does not support test2 from GUI client",wxLOG_Error
);
628 m_written
= -1; //wxSOCKET_LOST will interpret this as failure
631 else if (type
== 0xBE || type
== 0xDE)
633 m_size
= chunks
* (type
== 0xBE ? 1 : 1024);
634 m_inbuf
= new char[m_size
];
635 m_outbuf
= new char[m_size
];
639 LogWorker(wxString::Format("Message signature: len: %d, type: %s, size: %d (bytes)",chunks
,type
== 0xBE ? "b" : "kB",m_size
));
644 LogWorker(wxString::Format("Unknown test type %x",type
));
650 while(!m_socket
->Error() && (2 - m_infill
!= 0));
658 if (m_size
== m_infill
)
660 m_signature
[0] = m_signature
[1] = 0x0;
665 m_socket
->Read(m_inbuf
+ m_infill
,m_size
- m_infill
);
666 if (m_socket
->Error())
668 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
670 LogWorker(wxString::Format("Read error (%d): %s",
671 m_socket
->LastError(),
672 GetSocketErrorMsg(m_socket
->LastError())),
680 memcpy(m_outbuf
+m_outfill
,m_inbuf
+m_infill
,m_socket
->LastCount());
681 m_infill
+= m_socket
->LastCount();
682 m_outfill
+= m_socket
->LastCount();
686 while(!m_socket
->Error());
689 void EventWorker::OnSocketEvent(wxSocketEvent
& pEvent
)
691 switch(pEvent
.GetSocketEvent())
697 case wxSOCKET_OUTPUT
:
702 case wxSOCKET_CONNECTION
:
703 LogWorker("Unexpected wxSOCKET_CONNECTION in EventWorker", wxLOG_Error
);
708 LogWorker("Connection lost");
710 e
.m_workerFailed
= m_written
!= m_size
;
711 wxGetApp().AddPendingEvent(e
);
717 void EventWorker::DoWrite()
721 if (m_written
== m_size
)
725 LogWorker( "All data written");
728 if (m_outfill
- m_written
== 0)
732 m_socket
->Write(m_outbuf
+ m_written
,m_outfill
- m_written
);
733 if (m_socket
->Error())
735 if (m_socket
->LastError() != wxSOCKET_WOULDBLOCK
)
738 wxString::Format("Write error (%d): %s",
739 m_socket
->LastError(),
740 GetSocketErrorMsg(m_socket
->LastError())
748 LogWorker("Write would block, waiting for OUTPUT event");
753 memmove(m_outbuf
,m_outbuf
+m_socket
->LastCount(),m_outfill
-m_socket
->LastCount());
754 m_written
+= m_socket
->LastCount();
756 LogWorker(wxString::Format("Written %d of %d bytes, todo %d",
757 m_socket
->LastCount(),m_size
,m_size
- m_written
));
759 while (!m_socket
->Error());
762 BEGIN_EVENT_TABLE(EventWorker
,wxEvtHandler
)
763 EVT_SOCKET(wxID_ANY
,EventWorker::OnSocketEvent
)