Socket sample:
[wxWidgets.git] / samples / sockets / client.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: client.cpp
3 // Purpose: Client for wxSocket demo
4 // Author: Guillermo Rodriguez Garcia <guille@iies.es>
5 // Modified by:
6 // Created: 1999/09/19
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 Guillermo Rodriguez Garcia
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ==========================================================================
13 // declarations
14 // ==========================================================================
15
16 // --------------------------------------------------------------------------
17 // headers
18 // --------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 # pragma implementation "client.cpp"
22 # pragma interface "client.cpp"
23 #endif
24
25 // For compilers that support precompilation, includes "wx/wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 # pragma hdrstop
30 #endif
31
32 // for all others, include the necessary headers
33 #ifndef WX_PRECOMP
34 # include "wx/wx.h"
35 #endif
36
37 #include "wx/socket.h"
38 #include "wx/url.h"
39 #include "wx/protocol/http.h"
40 #include "wx/progdlg.h"
41
42 // --------------------------------------------------------------------------
43 // resources
44 // --------------------------------------------------------------------------
45
46 // the application icon
47 #if defined(__WXGTK__) || defined(__WXMOTIF__)
48 # include "mondrian.xpm"
49 #endif
50
51 // --------------------------------------------------------------------------
52 // classes
53 // --------------------------------------------------------------------------
54
55 // Define a new application type
56 class MyApp : public wxApp
57 {
58 public:
59 virtual bool OnInit();
60 };
61
62 // Define a new frame type: this is going to be our main frame
63 class MyFrame : public wxFrame
64 {
65 public:
66 MyFrame();
67 ~MyFrame();
68
69 // event handlers (these functions should _not_ be virtual)
70 void OnQuit(wxCommandEvent& event);
71 void OnAbout(wxCommandEvent& event);
72 void OnOpenConnection(wxCommandEvent& event);
73 void OnTest1(wxCommandEvent& event);
74 void OnTest2(wxCommandEvent& event);
75 void OnTest3(wxCommandEvent& event);
76 void OnCloseConnection(wxCommandEvent& event);
77 void OnSocketEvent(wxSocketEvent& event);
78 void OnDatagram(wxCommandEvent& event);
79
80 // convenience functions
81 void UpdateStatusBar();
82
83 private:
84 wxSocketClient *m_sock;
85 wxPanel *m_panel;
86 wxTextCtrl *m_text;
87 wxMenu *m_menuFile;
88 wxMenu *m_menuSocket;
89 wxMenu *m_menuDatagramSocket;
90 wxMenuBar *m_menuBar;
91 bool m_busy;
92
93 // any class wishing to process wxWindows events must use this macro
94 DECLARE_EVENT_TABLE()
95 };
96
97 // --------------------------------------------------------------------------
98 // constants
99 // --------------------------------------------------------------------------
100
101 // IDs for the controls and the menu commands
102 enum
103 {
104 // menu items
105 CLIENT_QUIT = 1000,
106 CLIENT_ABOUT,
107 CLIENT_OPEN,
108 CLIENT_TEST1,
109 CLIENT_TEST2,
110 CLIENT_TEST3,
111 CLIENT_CLOSE,
112 CLIENT_DGRAM,
113
114 // id for socket
115 SOCKET_ID
116 };
117
118 // --------------------------------------------------------------------------
119 // event tables and other macros for wxWindows
120 // --------------------------------------------------------------------------
121
122 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
123 EVT_MENU(CLIENT_QUIT, MyFrame::OnQuit)
124 EVT_MENU(CLIENT_ABOUT, MyFrame::OnAbout)
125 EVT_MENU(CLIENT_OPEN, MyFrame::OnOpenConnection)
126 EVT_MENU(CLIENT_TEST1, MyFrame::OnTest1)
127 EVT_MENU(CLIENT_TEST2, MyFrame::OnTest2)
128 EVT_MENU(CLIENT_TEST3, MyFrame::OnTest3)
129 EVT_MENU(CLIENT_CLOSE, MyFrame::OnCloseConnection)
130 EVT_MENU(CLIENT_DGRAM, MyFrame::OnDatagram)
131 EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent)
132 END_EVENT_TABLE()
133
134 IMPLEMENT_APP(MyApp)
135
136 // ==========================================================================
137 // implementation
138 // ==========================================================================
139
140 // --------------------------------------------------------------------------
141 // the application class
142 // --------------------------------------------------------------------------
143
144 bool MyApp::OnInit()
145 {
146 // Create the main application window
147 MyFrame *frame = new MyFrame();
148
149 // Show it and tell the application that it's our main window
150 frame->Show(TRUE);
151 SetTopWindow(frame);
152
153 // success
154 return TRUE;
155 }
156
157 // --------------------------------------------------------------------------
158 // main frame
159 // --------------------------------------------------------------------------
160
161 // frame constructor
162 MyFrame::MyFrame() : wxFrame((wxFrame *)NULL, -1,
163 _T("wxSocket demo: Client"),
164 wxDefaultPosition, wxSize(300, 200))
165 {
166 // Give the frame an icon
167 SetIcon(wxICON(mondrian));
168
169 // Make menus
170 m_menuFile = new wxMenu();
171 m_menuFile->Append(CLIENT_ABOUT, _T("&About...\tCtrl-A"), _T("Show about dialog"));
172 m_menuFile->AppendSeparator();
173 m_menuFile->Append(CLIENT_QUIT, _T("E&xit\tAlt-X"), _T("Quit client"));
174
175 m_menuSocket = new wxMenu();
176 m_menuSocket->Append(CLIENT_OPEN, _T("&Open session"), _T("Connect to server"));
177 m_menuSocket->AppendSeparator();
178 m_menuSocket->Append(CLIENT_TEST1, _T("Test &1"), _T("Test basic functionality"));
179 m_menuSocket->Append(CLIENT_TEST2, _T("Test &2"), _T("Test ReadMsg and WriteMsg"));
180 m_menuSocket->Append(CLIENT_TEST3, _T("Test &3"), _T("Test large data transfer"));
181 m_menuSocket->AppendSeparator();
182 m_menuSocket->Append(CLIENT_CLOSE, _T("&Close session"), _T("Close connection"));
183
184 m_menuDatagramSocket = new wxMenu();
185 m_menuDatagramSocket->Append(CLIENT_DGRAM, _T("Send Datagram"), _("Test UDP sockets"));
186
187 // Append menus to the menubar
188 m_menuBar = new wxMenuBar();
189 m_menuBar->Append(m_menuFile, _T("&File"));
190 m_menuBar->Append(m_menuSocket, _T("&Socket"));
191 m_menuBar->Append(m_menuDatagramSocket, _T("&DatagramSocket"));
192 SetMenuBar(m_menuBar);
193
194 // Status bar
195 CreateStatusBar(2);
196
197 // Make a panel with a textctrl in it
198 m_panel = new wxPanel(this, -1, wxPoint(0, 0), GetClientSize());
199 m_text = new wxTextCtrl(m_panel, -1,
200 _T("Welcome to wxSocket demo: Client\n")
201 _T("Client ready\n\n"),
202 wxPoint(0, 0), m_panel->GetClientSize(),
203 wxTE_MULTILINE | wxTE_READONLY);
204
205 // Create the socket
206 m_sock = new wxSocketClient();
207 m_sock->SetEventHandler(*this, SOCKET_ID);
208 m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG |
209 wxSOCKET_INPUT_FLAG |
210 wxSOCKET_LOST_FLAG);
211 m_sock->Notify(TRUE);
212
213 m_busy = FALSE;
214 UpdateStatusBar();
215 }
216
217 MyFrame::~MyFrame()
218 {
219 delete m_sock;
220 }
221
222 // event handlers
223
224 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
225 {
226 // TRUE is to force the frame to close
227 Close(TRUE);
228 }
229
230 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
231 {
232 wxMessageBox(_T("wxSocket demo: Client\n")
233 _T("(c) 1999 Guillermo Rodriguez Garcia\n"),
234 _T("About Client"),
235 wxOK | wxICON_INFORMATION, this);
236 }
237
238 void MyFrame::OnOpenConnection(wxCommandEvent& WXUNUSED(event))
239 {
240 wxIPV4address addr;
241
242 m_menuSocket->Enable(CLIENT_OPEN, FALSE);
243 m_menuSocket->Enable(CLIENT_CLOSE, FALSE);
244
245 // Ask server address
246 wxString hostname = wxGetTextFromUser(
247 _T("Enter the address of the wxSocket demo server:"),
248 _T("Connect ..."),
249 _T("localhost"));
250
251 addr.Hostname(hostname);
252 addr.Service(3000);
253
254 // Non-blocking connect
255 m_text->AppendText(_T("Trying to connect (timeout = 10 sec) ...\n"));
256 m_sock->Connect(addr, TRUE);
257 // m_sock->WaitOnConnect(10);
258
259 if (m_sock->IsConnected())
260 m_text->AppendText(_T("Succeeded ! Connection established\n"));
261 else
262 {
263 m_sock->Close();
264 m_text->AppendText(_T("Failed ! Unable to connect\n"));
265 wxMessageBox(_T("Can't connect to the specified host"), _T("Alert !"));
266 }
267
268 UpdateStatusBar();
269 }
270
271 void MyFrame::OnTest1(wxCommandEvent& WXUNUSED(event))
272 {
273 char *buf1;
274 char *buf2;
275 unsigned char len;
276
277 // Disable socket menu entries (exception: Close Session)
278 m_busy = TRUE;
279 UpdateStatusBar();
280
281 m_text->AppendText(_T("\n=== Test 1 begins ===\n"));
282
283 // Tell the server which test we are running
284 char c = 0xBE;
285 m_sock->Write(&c, 1);
286
287 // Send some data and read it back. We know the size of the
288 // buffer, so we can specify the exact number of bytes to be
289 // sent or received and use the wxSOCKET_WAITALL flag. Also,
290 // we have disabled menu entries which could interfere with
291 // the test, so we can safely avoid the wxSOCKET_BLOCK flag.
292 //
293 // First we send a byte with the length of the string, then
294 // we send the string itself (do NOT try to send any integral
295 // value larger than a byte "as is" across the network, or
296 // you might be in trouble! Ever heard about big and little
297 // endian computers?)
298 //
299 m_sock->SetFlags(wxSOCKET_WAITALL);
300
301 buf1 = _T("Test string (less than 256 chars!)");
302 len = wxStrlen(buf1) + 1;
303 buf2 = new char[len];
304
305 m_text->AppendText(_T("Sending a test buffer to the server ..."));
306 m_sock->Write((char *)&len, 1);
307 m_sock->Write(buf1, len);
308 m_text->AppendText(m_sock->Error() ? _T("failed !\n") : _T("done\n"));
309
310 m_text->AppendText(_T("Receiving the buffer back from server ..."));
311 m_sock->Read(buf2, len);
312 m_text->AppendText(m_sock->Error() ? _T("failed !\n") : _T("done\n"));
313
314 m_text->AppendText(_T("Comparing the two buffers ..."));
315 if (memcmp(buf1, buf2, len) != 0)
316 {
317 m_text->AppendText(_T("failed!\n"));
318 m_text->AppendText(_T("Test 1 failed !\n"));
319 }
320 else
321 {
322 m_text->AppendText(_T("done\n"));
323 m_text->AppendText(_T("Test 1 passed !\n"));
324 }
325 m_text->AppendText(_T("=== Test 1 ends ===\n"));
326
327 delete[] buf2;
328 m_busy = FALSE;
329 UpdateStatusBar();
330 }
331
332 void MyFrame::OnTest2(wxCommandEvent& WXUNUSED(event))
333 {
334 char *msg1;
335 char *msg2;
336 size_t len;
337
338 // Disable socket menu entries (exception: Close Session)
339 m_busy = TRUE;
340 UpdateStatusBar();
341
342 m_text->AppendText(_T("\n=== Test 2 begins ===\n"));
343
344 // Tell the server which test we are running
345 char c = 0xCE;
346 m_sock->Write(&c, 1);
347
348 // Here we use ReadMsg and WriteMsg to send messages with
349 // a header with size information. Also, the reception is
350 // event triggered, so we test input events as well.
351 //
352 // We need to set no flags here (ReadMsg and WriteMsg are
353 // not affected by flags)
354 //
355 m_sock->SetFlags(wxSOCKET_WAITALL);
356
357 wxString s = wxGetTextFromUser(
358 _T("Enter an arbitrary string to send to the server:"),
359 _T("Test 2 ..."),
360 _T("Yes I like wxWindows!"));
361
362 msg1 = (char *)s.c_str();
363 len = wxStrlen(msg1) + 1;
364 msg2 = new char[len];
365
366 m_text->AppendText(_T("Sending the string with WriteMsg ..."));
367 m_sock->WriteMsg(msg1, len);
368 m_text->AppendText(m_sock->Error() ? _T("failed !\n") : _T("done\n"));
369 m_text->AppendText(_T("Waiting for an event (timeout = 2 sec)\n"));
370
371 // Wait until data available (will also return if the connection is lost)
372 m_sock->WaitForRead(2);
373
374 if (m_sock->IsData())
375 {
376 m_text->AppendText(_T("Reading the string back with ReadMsg ..."));
377 m_sock->ReadMsg(msg2, len);
378 m_text->AppendText(m_sock->Error() ? _T("failed !\n") : _T("done\n"));
379 m_text->AppendText(_T("Comparing the two buffers ..."));
380 if (memcmp(msg1, msg2, len) != 0)
381 {
382 m_text->AppendText(_T("failed!\n"));
383 m_text->AppendText(_T("Test 2 failed !\n"));
384 }
385 else
386 {
387 m_text->AppendText(_T("done\n"));
388 m_text->AppendText(_T("Test 2 passed !\n"));
389 }
390 }
391 else
392 m_text->AppendText(_T("Timeout ! Test 2 failed.\n"));
393
394 m_text->AppendText(_T("=== Test 2 ends ===\n"));
395
396 delete[] msg2;
397 m_busy = FALSE;
398 UpdateStatusBar();
399 }
400
401 void MyFrame::OnTest3(wxCommandEvent& WXUNUSED(event))
402 {
403 char *buf1;
404 char *buf2;
405 unsigned char len;
406
407 // Disable socket menu entries (exception: Close Session)
408 m_busy = TRUE;
409 UpdateStatusBar();
410
411 m_text->AppendText(_T("\n=== Test 3 begins ===\n"));
412
413 // Tell the server which test we are running
414 char c = 0xDE;
415 m_sock->Write(&c, 1);
416
417 // This test also is similar to the first one but it sends a
418 // large buffer so that wxSocket is actually forced to split
419 // it into pieces and take care of sending everything before
420 // returning.
421 //
422 m_sock->SetFlags(wxSOCKET_WAITALL);
423
424 // Note that len is in kbytes here!
425 len = 32;
426 buf1 = new char[len * 1024];
427 buf2 = new char[len * 1024];
428
429 for (int i = 0; i < len * 1024; i ++)
430 buf1[i] = (char)(i % 256);
431
432 m_text->AppendText(_T("Sending a large buffer (32K) to the server ..."));
433 m_sock->Write((char *)&len, 1);
434 m_sock->Write(buf1, len * 1024);
435 m_text->AppendText(m_sock->Error() ? _T("failed !\n") : _T("done\n"));
436
437 m_text->AppendText(_T("Receiving the buffer back from server ..."));
438 m_sock->Read(buf2, len * 1024);
439 m_text->AppendText(m_sock->Error() ? _T("failed !\n") : _T("done\n"));
440
441 m_text->AppendText(_T("Comparing the two buffers ..."));
442 if (memcmp(buf1, buf2, len) != 0)
443 {
444 m_text->AppendText(_T("failed!\n"));
445 m_text->AppendText(_T("Test 3 failed !\n"));
446 }
447 else
448 {
449 m_text->AppendText(_T("done\n"));
450 m_text->AppendText(_T("Test 3 passed !\n"));
451 }
452 m_text->AppendText(_T("=== Test 3 ends ===\n"));
453
454 delete[] buf2;
455 m_busy = FALSE;
456 UpdateStatusBar();
457 }
458
459 void MyFrame::OnCloseConnection(wxCommandEvent& WXUNUSED(event))
460 {
461 m_sock->Close();
462 UpdateStatusBar();
463 }
464
465 void MyFrame::OnDatagram(wxCommandEvent& WXUNUSED(event))
466 {
467 }
468
469 void MyFrame::OnSocketEvent(wxSocketEvent& event)
470 {
471 wxString s = _T("OnSocketEvent: ");
472
473 switch(event.SocketEvent())
474 {
475 case wxSOCKET_INPUT : s.Append(_T("wxSOCKET_INPUT\n")); break;
476 case wxSOCKET_LOST : s.Append(_T("wxSOCKET_LOST\n")); break;
477 case wxSOCKET_CONNECTION : s.Append(_T("wxSOCKET_CONNECTION\n")); break;
478 default : s.Append(_T("Unexpected event !\n")); break;
479 }
480
481 m_text->AppendText(s);
482 UpdateStatusBar();
483 }
484
485 // convenience functions
486
487 void MyFrame::UpdateStatusBar()
488 {
489 wxString s;
490
491 if (!m_sock->IsConnected())
492 {
493 s.Printf(_T("Not connected"));
494 }
495 else
496 {
497 wxIPV4address addr;
498
499 m_sock->GetPeer(addr);
500 s.Printf(_T("%s : %d"), (addr.Hostname()).c_str(), addr.Service());
501 }
502
503 SetStatusText(s, 1);
504
505 m_menuSocket->Enable(CLIENT_OPEN, !m_sock->IsConnected() && !m_busy);
506 m_menuSocket->Enable(CLIENT_TEST1, m_sock->IsConnected() && !m_busy);
507 m_menuSocket->Enable(CLIENT_TEST2, m_sock->IsConnected() && !m_busy);
508 m_menuSocket->Enable(CLIENT_TEST3, m_sock->IsConnected() && !m_busy);
509 m_menuSocket->Enable(CLIENT_CLOSE, m_sock->IsConnected());
510 }