1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        samples/sockbase/client.cpp
3 // Purpose:     Sockets sample for wxBase
4 // Author:      Lukasz Michalski
5 // Modified by:
6 // Created:     27.06.2005
7 // Copyright:   (c) 2005 Lukasz Michalski <lmichalski@sf.net>
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 #include "wx/wx.h"
20 #include "wx/socket.h"
21 #include "wx/event.h"
22 #include "wx/list.h"
23 #include "wx/cmdline.h"
24 #include "wx/ffile.h"
25 #include "wx/datetime.h"
26 #include "wx/timer.h"
27 #include "wx/thread.h"
28 
29 const wxEventType wxEVT_WORKER = wxNewEventType();
30 #define EVT_WORKER(func) DECLARE_EVENT_TABLE_ENTRY( wxEVT_WORKER, -1, -1, (wxObjectEventFunction) (wxEventFunction) (WorkerEventFunction) & func, (wxObject *) NULL ),
31 
32 const int timeout_val = 1000;
33 
34 class WorkerEvent : public wxEvent {
35 public:
36     typedef enum {
37         CONNECTING,
38         SENDING,
39         RECEIVING,
40         DISCONNECTING,
41         DONE
42     } evt_type;
WorkerEvent(void * pSender,evt_type type)43     WorkerEvent(void* pSender, evt_type type)
44     {
45         SetId(-1);
46         SetEventType(wxEVT_WORKER);
47         m_sender = pSender;
48         m_eventType = type;
49         m_isFailed = false;
50     }
51 
setFailed()52     void setFailed() { m_isFailed = true; }
isFailed() const53     bool isFailed() const { return m_isFailed; }
54 
Clone() const55     virtual wxEvent* Clone() const
56     {
57         return new WorkerEvent(*this);
58     }
59     void* m_sender;
60     bool m_isFailed;
61     wxString m_workerIdent;
62     evt_type m_eventType;
63 };
64 
65 typedef void (wxEvtHandler::*WorkerEventFunction)(WorkerEvent&);
66 
67 class ThreadWorker;
68 class EventWorker;
69 
70 WX_DECLARE_LIST(ThreadWorker, TList);
71 WX_DECLARE_LIST(EventWorker, EList);
72 
73 class Client : public wxApp {
74     wxDECLARE_EVENT_TABLE();
75 public:
76     void RemoveEventWorker(EventWorker* p_worker);
77 private:
78     typedef enum
79     {
80       THREADS,
81       EVENTS
82     } workMode;
83 
84     typedef enum
85     {
86       SEND_RANDOM,
87       SEND_MESSAGE,
88       STRESS_TEST
89     } sendType;
90 
91     workMode m_workMode;
92     sendType m_sendType;
93     wxString m_message;
94     wxString m_host;
95     long m_stressWorkers;
96 
97     virtual bool OnInit();
98     virtual int OnRun();
99     virtual int OnExit();
100     void OnInitCmdLine(wxCmdLineParser& pParser);
101     bool OnCmdLineParsed(wxCmdLineParser& pParser);
102     void OnWorkerEvent(WorkerEvent& pEvent);
103     void OnTimerEvent(wxTimerEvent& pEvent);
104 
105     void StartWorker(workMode pMode, const wxString& pMessage);
106     void StartWorker(workMode pMode);
107     char* CreateBuffer(int *msgsize);
108 
109     void dumpStatistics();
110 
111     TList m_threadWorkers;
112     EList m_eventWorkers;
113 
114     unsigned m_statConnecting;
115     unsigned m_statSending;
116     unsigned m_statReceiving;
117     unsigned m_statDisconnecting;
118     unsigned m_statDone;
119     unsigned m_statFailed;
120 
121     wxTimer mTimer;
122 };
123 
124 DECLARE_APP(Client);
125 
126 class ThreadWorker : public wxThread
127 {
128 public:
129     ThreadWorker(const wxString& p_host, char* p_buf, int p_size);
130     virtual ExitCode Entry();
131 private:
132     wxString m_host;
133     wxSocketClient* m_clientSocket;
134     char* m_inbuf;
135     char* m_outbuf;
136     int m_outsize;
137     int m_insize;
138     wxString m_workerIdent;
139 };
140 
141 class EventWorker : public wxEvtHandler
142 {
143     wxDECLARE_EVENT_TABLE();
144 public:
145     EventWorker(const wxString& p_host, char* p_buf, int p_size);
146     void Run();
147     virtual ~EventWorker();
148 private:
149     wxString m_host;
150     wxSocketClient* m_clientSocket;
151     char* m_inbuf;
152     char* m_outbuf;
153     int m_outsize;
154     int m_written;
155     int m_insize;
156     int m_readed;
157 
158     WorkerEvent::evt_type m_currentType;
159     bool m_doneSent;
160     wxIPV4address m_localaddr;
161 
162     void OnSocketEvent(wxSocketEvent& pEvent);
163     void SendEvent(bool failed);
164 };
165 
166 /******************* Implementation ******************/
167 IMPLEMENT_APP_CONSOLE(Client);
168 
169 #include <wx/listimpl.cpp>
170 WX_DEFINE_LIST(TList);
171 WX_DEFINE_LIST(EList);
172 
173 wxString
CreateIdent(const wxIPV4address & addr)174 CreateIdent(const wxIPV4address& addr)
175 {
176     return wxString::Format(wxT("%s:%d"),addr.IPAddress().c_str(),addr.Service());
177 }
178 
179 void
OnInitCmdLine(wxCmdLineParser & pParser)180 Client::OnInitCmdLine(wxCmdLineParser& pParser)
181 {
182     wxApp::OnInitCmdLine(pParser);
183     pParser.AddSwitch(wxT("e"),wxT("event"),_("Use event based worker (default)"),wxCMD_LINE_PARAM_OPTIONAL);
184     pParser.AddSwitch(wxT("t"),wxT("thread"),_("Use thread based worker"),wxCMD_LINE_PARAM_OPTIONAL);
185     pParser.AddSwitch(wxT("r"),wxT("random"),_("Send radnom data (default)"),wxCMD_LINE_PARAM_OPTIONAL);
186     pParser.AddOption(wxT("m"),wxT("message"),_("Send message from <str>"),wxCMD_LINE_VAL_STRING,wxCMD_LINE_PARAM_OPTIONAL);
187     pParser.AddOption(wxT("f"),wxT("file"),_("Send contents of <file>"),wxCMD_LINE_VAL_STRING,wxCMD_LINE_PARAM_OPTIONAL);
188     pParser.AddOption(wxT("H"),wxT("hostname"),_("IP or name of host to connect to"),wxCMD_LINE_VAL_STRING,wxCMD_LINE_PARAM_OPTIONAL);
189     pParser.AddOption(wxT("s"),wxT("stress"),_("stress test with <num> concurrent connections"),wxCMD_LINE_VAL_NUMBER,wxCMD_LINE_PARAM_OPTIONAL);
190 }
191 
192 
193 bool
OnCmdLineParsed(wxCmdLineParser & pParser)194 Client::OnCmdLineParsed(wxCmdLineParser& pParser)
195 {
196     wxString fname;
197     m_workMode = EVENTS;
198     m_stressWorkers = 50;
199 
200     if (pParser.Found(_("verbose")))
201     {
202         wxLog::AddTraceMask(wxT("wxSocket"));
203         wxLog::AddTraceMask(wxT("epolldispatcher"));
204         wxLog::AddTraceMask(wxT("selectdispatcher"));
205         wxLog::AddTraceMask(wxT("thread"));
206         wxLog::AddTraceMask(wxT("events"));
207     }
208 
209     if (pParser.Found(wxT("t")))
210         m_workMode = THREADS;
211     m_sendType = SEND_RANDOM;
212 
213     if (pParser.Found(wxT("m"),&m_message))
214         m_sendType = SEND_MESSAGE;
215     else if (pParser.Found(wxT("f"),&fname))
216     {
217         wxFFile file(fname);
218         if (!file.IsOpened()) {
219             wxLogError(wxT("Cannot open file %s"),fname.c_str());
220             return false;
221         };
222         if (!file.ReadAll(&m_message)) {
223             wxLogError(wxT("Cannot read conten of file %s"),fname.c_str());
224             return false;
225         };
226         m_sendType = SEND_MESSAGE;
227     };
228 
229     if (pParser.Found(wxT("s"),&m_stressWorkers))
230         m_sendType = STRESS_TEST;
231 
232     m_host = wxT("127.0.0.1");
233     pParser.Found(wxT("H"),&m_host);
234     return wxApp::OnCmdLineParsed(pParser);
235 };
236 
237 bool
OnInit()238 Client::OnInit()
239 {
240     if (!wxApp::OnInit())
241         return false;
242     srand(wxDateTime::Now().GetTicks());
243     mTimer.SetOwner(this);
244     m_statConnecting = 0;
245     m_statSending = 0;
246     m_statReceiving = 0;
247     m_statDisconnecting = 0;
248     m_statDone = 0;
249     m_statFailed = 0;
250     return true;
251 }
252 
253 int
OnRun()254 Client::OnRun()
255 {
256     int i;
257     switch(m_sendType)
258     {
259         case STRESS_TEST:
260             switch(m_workMode)
261             {
262                 case THREADS:
263                     for (i = 0; i < m_stressWorkers; i++) {
264                         if (m_message.empty())
265                             StartWorker(THREADS);
266                         else
267                             StartWorker(THREADS, m_message);
268                     }
269                     break;
270                 case EVENTS:
271                     for (i = 0; i < m_stressWorkers; i++) {
272                         if (m_message.empty())
273                             StartWorker(EVENTS);
274                         else
275                             StartWorker(EVENTS, m_message);
276                     }
277                     break;
278                 default:
279                     for (i = 0; i < m_stressWorkers; i++) {
280                         if (m_message.empty())
281                             StartWorker(i % 5 == 0 ? THREADS : EVENTS);
282                         else
283                             StartWorker(i % 5 == 0 ? THREADS : EVENTS, m_message);
284                     }
285                 break;
286             }
287         break;
288         case SEND_MESSAGE:
289             StartWorker(m_workMode,m_message);
290         break;
291         case SEND_RANDOM:
292             StartWorker(m_workMode);
293         break;
294     }
295     mTimer.Start(timeout_val,true);
296     return wxApp::OnRun();
297 }
298 
299 int
OnExit()300 Client::OnExit()
301 {
302     for(EList::compatibility_iterator it = m_eventWorkers.GetFirst(); it ; it->GetNext()) {
303         delete it->GetData();
304     }
305     return 0;
306 }
307 
308 // Create buffer to be sent by client. Buffer contains test indicator
309 // message size and place for data
310 // msgsize parameter contains size of data in bytes and
311 // if input value does not fit into 250 bytes then
312 // on exit is updated to new value that is multiply of 1024 bytes
313 char*
CreateBuffer(int * msgsize)314 Client::CreateBuffer(int* msgsize)
315 {
316     int bufsize = 0;
317     char* buf;
318     //if message should have more than 256 bytes then set it as
319     //test3 for compatibility with GUI server sample
320     if ((*msgsize) > 250)
321     {
322         //send at least one kb of data
323         int size = (*msgsize)/1024 + 1;
324         //returned buffer will contain test indicator, message size in kb and data
325         bufsize = size*1024+2;
326         buf = new char[bufsize];
327         buf[0] = (unsigned char)0xDE; //second byte contains size in kilobytes
328         buf[1] = (char)(size);
329         *msgsize = size*1024;
330     }
331     else
332     {
333         //returned buffer will contain test indicator, message size in kb and data
334         bufsize = (*msgsize)+2;
335         buf = new char[bufsize];
336         buf[0] = (unsigned char)0xBE; //second byte contains size in bytes
337         buf[1] = (char)(*msgsize);
338     }
339     return buf;
340 }
341 
342 void
StartWorker(workMode pMode)343 Client::StartWorker(workMode pMode) {
344     int msgsize = 1 + (int) (250000.0 * (rand() / (RAND_MAX + 1.0)));
345     char* buf = CreateBuffer(&msgsize);
346 
347     //fill data part of buffer with random bytes
348     for (int i = 2; i < (msgsize); i++) {
349         buf[i] = i % 256;
350     }
351 
352     if (pMode == THREADS) {
353         ThreadWorker* c = new ThreadWorker(m_host,buf,msgsize+2);
354         if (c->Create() != wxTHREAD_NO_ERROR) {
355             wxLogError(wxT("Cannot create more threads"));
356         } else {
357             c->Run();
358             m_threadWorkers.Append(c);
359         }
360     } else {
361         EventWorker* e = new EventWorker(m_host,buf,msgsize+2);
362         e->Run();
363         m_eventWorkers.Append(e);
364     }
365     m_statConnecting++;
366 }
367 
368 void
StartWorker(workMode pMode,const wxString & pMessage)369 Client::StartWorker(workMode pMode, const wxString& pMessage) {
370     char* tmpbuf = wxStrdup(pMessage.mb_str());
371     int msgsize = strlen(tmpbuf);
372     char* buf = CreateBuffer(&msgsize);
373     memset(buf+2,0x0,msgsize);
374     memcpy(buf+2,tmpbuf,msgsize);
375     free(tmpbuf);
376 
377     if (pMode == THREADS) {
378         ThreadWorker* c = new ThreadWorker(m_host,buf,msgsize+2);
379         if (c->Create() != wxTHREAD_NO_ERROR) {
380             wxLogError(wxT("Cannot create more threads"));
381         } else {
382             c->Run();
383             m_threadWorkers.Append(c);
384         }
385     } else {
386         EventWorker* e = new EventWorker(m_host,buf,msgsize+2);
387         e->Run();
388         m_eventWorkers.Append(e);
389     }
390     m_statConnecting++;
391 }
392 
393 void
OnWorkerEvent(WorkerEvent & pEvent)394 Client::OnWorkerEvent(WorkerEvent& pEvent) {
395     switch (pEvent.m_eventType) {
396         case WorkerEvent::CONNECTING:
397             if (pEvent.isFailed())
398             {
399                 m_statConnecting--;
400                 m_statFailed++;
401             }
402         break;
403         case WorkerEvent::SENDING:
404             if (pEvent.isFailed())
405             {
406                 m_statFailed++;
407                 m_statSending--;
408             }
409             else
410             {
411                 m_statConnecting--;
412                 m_statSending++;
413             }
414         break;
415         case WorkerEvent::RECEIVING:
416             if (pEvent.isFailed())
417             {
418                 m_statReceiving--;
419                 m_statFailed++;
420             }
421             else
422             {
423                 m_statSending--;
424                 m_statReceiving++;
425             }
426         break;
427         case WorkerEvent::DISCONNECTING:
428             if (pEvent.isFailed())
429             {
430                 m_statDisconnecting--;
431                 m_statFailed++;
432             }
433             else
434             {
435                 m_statReceiving--;
436                 m_statDisconnecting++;
437             }
438         break;
439         case WorkerEvent::DONE:
440             m_statDone++;
441             m_statDisconnecting--;
442         break;
443     };
444 
445     if (pEvent.isFailed() || pEvent.m_eventType == WorkerEvent::DONE)
446     {
447         for(TList::compatibility_iterator it = m_threadWorkers.GetFirst(); it ; it = it->GetNext()) {
448             if (it->GetData() == pEvent.m_sender) {
449                 m_threadWorkers.DeleteNode(it);
450                 break;
451             }
452         }
453         for(EList::compatibility_iterator it2 = m_eventWorkers.GetFirst(); it2 ; it2 = it2->GetNext())
454         {
455             if (it2->GetData() == pEvent.m_sender) {
456                 delete it2->GetData();
457                 m_eventWorkers.DeleteNode(it2);
458                 break;
459             }
460         }
461         if ((m_threadWorkers.GetCount() == 0) && (m_eventWorkers.GetCount() == 0))
462         {
463             mTimer.Stop();
464             dumpStatistics();
465             wxSleep(2);
466             ExitMainLoop();
467         }
468         else
469         {
470             mTimer.Start(timeout_val,true);
471         }
472     }
473 }
474 
475 void
RemoveEventWorker(EventWorker * p_worker)476 Client::RemoveEventWorker(EventWorker* p_worker) {
477     for(EList::compatibility_iterator it = m_eventWorkers.GetFirst(); it ; it = it->GetNext()) {
478         if (it->GetData() == p_worker) {
479             //wxLogDebug(wxT("Deleting event worker"));
480             delete it->GetData();
481             m_eventWorkers.DeleteNode(it);
482             return;
483         }
484     }
485 }
486 
487 void
dumpStatistics()488 Client::dumpStatistics() {
489     wxString msg(
490         wxString::Format(_("Connecting:\t%d\nSending\t\t%d\nReceiving\t%d\nDisconnecting:\t%d\nDone:\t\t%d\nFailed:\t\t%d\n"),
491                 m_statConnecting,
492                 m_statSending,
493                 m_statReceiving,
494                 m_statDisconnecting,
495                 m_statDone,
496                 m_statFailed
497                 ));
498 
499     wxLogMessage(wxT("Current status:\n%s\n"),msg.c_str());
500 }
501 
502 void
OnTimerEvent(wxTimerEvent &)503 Client::OnTimerEvent(wxTimerEvent&) {
504     dumpStatistics();
505 }
506 
wxBEGIN_EVENT_TABLE(Client,wxEvtHandler)507 wxBEGIN_EVENT_TABLE(Client,wxEvtHandler)
508     EVT_WORKER(Client::OnWorkerEvent)
509     EVT_TIMER(wxID_ANY,Client::OnTimerEvent)
510 wxEND_EVENT_TABLE()
511 
512 
513 
514 EventWorker::EventWorker(const wxString& p_host, char* p_buf, int p_size)
515   : m_host(p_host),
516     m_outbuf(p_buf),
517     m_outsize(p_size),
518     m_written(0),
519     m_readed(0)
520 {
521     m_clientSocket = new wxSocketClient(wxSOCKET_NOWAIT);
522     m_clientSocket->SetEventHandler(*this);
523     m_insize = m_outsize - 2;
524     m_inbuf = new char[m_insize];
525 }
526 
527 void
Run()528 EventWorker::Run() {
529     wxIPV4address ca;
530     ca.Hostname(m_host);
531     ca.Service(3000);
532     m_clientSocket->SetNotify(wxSOCKET_CONNECTION_FLAG|wxSOCKET_LOST_FLAG|wxSOCKET_OUTPUT_FLAG|wxSOCKET_INPUT_FLAG);
533     m_clientSocket->Notify(true);
534     m_currentType = WorkerEvent::CONNECTING;
535     m_doneSent = false;
536     //wxLogMessage(wxT("EventWorker: Connecting....."));
537     m_clientSocket->Connect(ca,false);
538 }
539 
540 void
OnSocketEvent(wxSocketEvent & pEvent)541 EventWorker::OnSocketEvent(wxSocketEvent& pEvent) {
542     switch(pEvent.GetSocketEvent()) {
543         case wxSOCKET_INPUT:
544             //wxLogDebug(wxT("EventWorker: INPUT"));
545             do {
546                 if (m_readed == m_insize)
547                     return; //event already posted
548                 m_clientSocket->Read(m_inbuf + m_readed, m_insize - m_readed);
549                 if (m_clientSocket->Error())
550                 {
551                     if (m_clientSocket->LastError() != wxSOCKET_WOULDBLOCK)
552                     {
553                         wxLogError(wxT("%s: read error"),CreateIdent(m_localaddr).c_str());
554                         SendEvent(true);
555                     }
556                 }
557 
558                 m_readed += m_clientSocket->LastCount();
559                 //wxLogDebug(wxT("EventWorker: readed %d bytes, %d bytes to do"),m_clientSocket->LastCount(), m_insize - m_readed);
560                 if (m_readed == m_insize)
561                 {
562                     if (!memcmp(m_inbuf,m_outbuf,m_insize)) {
563                         wxLogError(wxT("%s: data mismatch"),CreateIdent(m_localaddr).c_str());
564                         SendEvent(true);
565                     }
566                     m_currentType = WorkerEvent::DISCONNECTING;
567                     wxLogDebug(wxT("%s: DISCONNECTING"),CreateIdent(m_localaddr).c_str());
568                     SendEvent(false);
569 
570                     //wxLogDebug(wxT("EventWorker %p closing"),this);
571                     m_clientSocket->Close();
572 
573                     m_currentType = WorkerEvent::DONE;
574                     wxLogDebug(wxT("%s: DONE"),CreateIdent(m_localaddr).c_str());
575                     SendEvent(false);
576                 }
577             } while (!m_clientSocket->Error());
578         break;
579         case wxSOCKET_OUTPUT:
580             //wxLogDebug(wxT("EventWorker: OUTPUT"));
581             do {
582                 if (m_written == m_outsize)
583                     return;
584                 if (m_written == 0)
585                 {
586                     m_currentType = WorkerEvent::SENDING;
587                     wxLogDebug(wxT("%s: SENDING"),CreateIdent(m_localaddr).c_str());
588                 }
589                 m_clientSocket->Write(m_outbuf + m_written, m_outsize - m_written);
590                 if (m_clientSocket->Error())
591                 {
592                     if (m_clientSocket->LastError() != wxSOCKET_WOULDBLOCK) {
593                         wxLogError(wxT("%s: Write error"),CreateIdent(m_localaddr).c_str());
594                         SendEvent(true);
595                     }
596                 }
597                 m_written += m_clientSocket->LastCount();
598                 if (m_written != m_outsize)
599                 {
600                     //wxLogDebug(wxT("EventWorker: written %d bytes, %d bytes to do"),m_clientSocket->LastCount(),m_outsize - m_written);
601                 }
602                 else
603                 {
604                     //wxLogDebug(wxT("EventWorker %p SENDING->RECEIVING"),this);
605                     m_currentType = WorkerEvent::RECEIVING;
606                     wxLogDebug(wxT("%s: RECEIVING"),CreateIdent(m_localaddr).c_str());
607                     SendEvent(false);
608                 }
609             } while(!m_clientSocket->Error());
610         break;
611         case wxSOCKET_CONNECTION:
612         {
613             //wxLogMessage(wxT("EventWorker: got connection"));
614             wxLogMessage(wxT("%s: starting writing message (2 bytes for signature and %d bytes of data to write)"),CreateIdent(m_localaddr).c_str(),m_outsize-2);
615             if (!m_clientSocket->GetLocal(m_localaddr))
616             {
617                 wxLogError(_("Cannot get peer data for socket %p"),m_clientSocket);
618             }
619             m_currentType = WorkerEvent::SENDING;
620             wxLogDebug(wxT("%s: CONNECTING"),CreateIdent(m_localaddr).c_str());
621             SendEvent(false);
622         }
623         break;
624         case wxSOCKET_LOST:
625         {
626             wxLogError(_("%s: connection lost"),CreateIdent(m_localaddr).c_str());
627             SendEvent(true);
628         }
629         break;
630     }
631 }
632 
633 void
SendEvent(bool failed)634 EventWorker::SendEvent(bool failed) {
635     if (m_doneSent)
636         return;
637     WorkerEvent e(this,m_currentType);
638     if (failed) e.setFailed();
639     wxGetApp().AddPendingEvent(e);
640     m_doneSent = failed || m_currentType == WorkerEvent::DONE;
641 };
642 
~EventWorker()643 EventWorker::~EventWorker() {
644     m_clientSocket->Destroy();
645     delete [] m_outbuf;
646     delete [] m_inbuf;
647 }
648 
wxBEGIN_EVENT_TABLE(EventWorker,wxEvtHandler)649 wxBEGIN_EVENT_TABLE(EventWorker,wxEvtHandler)
650     EVT_SOCKET(wxID_ANY,EventWorker::OnSocketEvent)
651 wxEND_EVENT_TABLE()
652 
653 
654 ThreadWorker::ThreadWorker(const wxString& p_host, char* p_buf, int p_size)
655   : wxThread(wxTHREAD_DETACHED),
656     m_host(p_host),
657     m_outbuf(p_buf),
658     m_outsize(p_size)
659 {
660     m_clientSocket = new wxSocketClient(wxSOCKET_BLOCK|wxSOCKET_WAITALL);
661     m_insize = m_outsize - 2;
662     m_inbuf = new char[m_insize];
663 }
664 
Entry()665 wxThread::ExitCode ThreadWorker::Entry()
666 {
667     wxIPV4address ca;
668     ca.Hostname(m_host);
669     ca.Service(5678);
670     //wxLogDebug(wxT("ThreadWorker: Connecting....."));
671     m_clientSocket->SetTimeout(60);
672     bool failed = false;
673     WorkerEvent::evt_type etype = WorkerEvent::CONNECTING;
674     if (!m_clientSocket->Connect(ca)) {
675         wxLogError(wxT("Cannot connect to %s:%d"),ca.IPAddress().c_str(), ca.Service());
676         failed = true;
677     } else {
678         //wxLogMessage(wxT("ThreadWorker: Connected. Sending %d bytes of data"),m_outsize);
679         etype = WorkerEvent::SENDING;
680         WorkerEvent e(this,etype);
681         wxGetApp().AddPendingEvent(e);
682         int to_process = m_outsize;
683         do {
684             m_clientSocket->Write(m_outbuf,m_outsize);
685             if (m_clientSocket->Error()) {
686                 wxLogError(wxT("ThreadWorker: Write error"));
687                 failed  = true;
688             }
689             to_process -= m_clientSocket->LastCount();
690             //wxLogDebug(wxT("EventWorker: written %d bytes, %d bytes to do"),m_clientSocket->LastCount(),to_process);
691         } while(!m_clientSocket->Error() && to_process != 0);
692 
693         if (!failed) {
694             etype = WorkerEvent::RECEIVING;
695             WorkerEvent e(this,etype);
696             wxGetApp().AddPendingEvent(e);
697             to_process = m_insize;
698             do {
699                 m_clientSocket->Read(m_inbuf,m_insize);
700                 if (m_clientSocket->Error()) {
701                     wxLogError(wxT("ThreadWorker: Read error"));
702                     failed = true;
703                     break;
704                 }
705                 to_process -= m_clientSocket->LastCount();
706                 //wxLogDebug(wxT("EventWorker: readed %d bytes, %d bytes to do"),m_clientSocket->LastCount(),to_process);
707             } while(!m_clientSocket->Error() && to_process != 0);
708         }
709 
710         char* outdat = (char*)m_outbuf+2;
711         if (!failed && (memcmp(m_inbuf,outdat,m_insize) != 0))
712         {
713             wxLogError(wxT("Data mismatch"));
714             failed = true;
715         }
716     }
717     //wxLogDebug(wxT("ThreadWorker: Finished"));
718     if (!failed) {
719         etype = WorkerEvent::DISCONNECTING;
720         WorkerEvent e(this,etype);
721         wxGetApp().AddPendingEvent(e);
722     };
723     m_clientSocket->Close();
724     m_clientSocket->Destroy();
725     m_clientSocket = NULL;
726     delete [] m_outbuf;
727     delete [] m_inbuf;
728     if (!failed)
729         etype = WorkerEvent::DONE;
730     WorkerEvent e(this,etype);
731     if (failed) e.setFailed();
732     wxGetApp().AddPendingEvent(e);
733     return 0;
734 }
735 
736