URL test and some comments added
[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/wfstream.h"
40
41 // --------------------------------------------------------------------------
42 // resources
43 // --------------------------------------------------------------------------
44
45 // the application icon
46 #if defined(__WXGTK__) || defined(__WXMOTIF__)
47 # include "mondrian.xpm"
48 #endif
49
50 // --------------------------------------------------------------------------
51 // classes
52 // --------------------------------------------------------------------------
53
54 // Define a new application type
55 class MyApp : public wxApp
56 {
57 public:
58 virtual bool OnInit();
59 };
60
61 // Define a new frame type: this is going to be our main frame
62 class MyFrame : public wxFrame
63 {
64 public:
65 MyFrame();
66 ~MyFrame();
67
68 // event handlers for File menu
69 void OnQuit(wxCommandEvent& event);
70 void OnAbout(wxCommandEvent& event);
71
72 // event handlers for Socket menu
73 void OnOpenConnection(wxCommandEvent& event);
74 void OnTest1(wxCommandEvent& event);
75 void OnTest2(wxCommandEvent& event);
76 void OnTest3(wxCommandEvent& event);
77 void OnCloseConnection(wxCommandEvent& event);
78
79 // event handlers for Protocols menu
80 void OnTestURL(wxCommandEvent& event);
81
82 // event handlers for DatagramSocket menu
83 void OnDatagram(wxCommandEvent& event);
84
85 // socket event handler
86 void OnSocketEvent(wxSocketEvent& event);
87
88 // convenience functions
89 void UpdateStatusBar();
90
91 private:
92 wxSocketClient *m_sock;
93 wxPanel *m_panel;
94 wxTextCtrl *m_text;
95 wxMenu *m_menuFile;
96 wxMenu *m_menuSocket;
97 wxMenu *m_menuDatagramSocket;
98 wxMenu *m_menuProtocols;
99 wxMenuBar *m_menuBar;
100 bool m_busy;
101
102 // any class wishing to process wxWindows events must use this macro
103 DECLARE_EVENT_TABLE()
104 };
105
106 // --------------------------------------------------------------------------
107 // constants
108 // --------------------------------------------------------------------------
109
110 // IDs for the controls and the menu commands
111 enum
112 {
113 // menu items
114 CLIENT_QUIT = 1000,
115 CLIENT_ABOUT,
116 CLIENT_OPEN,
117 CLIENT_TEST1,
118 CLIENT_TEST2,
119 CLIENT_TEST3,
120 CLIENT_CLOSE,
121 CLIENT_TESTURL,
122 CLIENT_DGRAM,
123
124 // id for socket
125 SOCKET_ID
126 };
127
128 // --------------------------------------------------------------------------
129 // event tables and other macros for wxWindows
130 // --------------------------------------------------------------------------
131
132 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
133 EVT_MENU(CLIENT_QUIT, MyFrame::OnQuit)
134 EVT_MENU(CLIENT_ABOUT, MyFrame::OnAbout)
135 EVT_MENU(CLIENT_OPEN, MyFrame::OnOpenConnection)
136 EVT_MENU(CLIENT_TEST1, MyFrame::OnTest1)
137 EVT_MENU(CLIENT_TEST2, MyFrame::OnTest2)
138 EVT_MENU(CLIENT_TEST3, MyFrame::OnTest3)
139 EVT_MENU(CLIENT_CLOSE, MyFrame::OnCloseConnection)
140 EVT_MENU(CLIENT_DGRAM, MyFrame::OnDatagram)
141 EVT_MENU(CLIENT_TESTURL, MyFrame::OnTestURL)
142 EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent)
143 END_EVENT_TABLE()
144
145 IMPLEMENT_APP(MyApp)
146
147 // ==========================================================================
148 // implementation
149 // ==========================================================================
150
151 // --------------------------------------------------------------------------
152 // the application class
153 // --------------------------------------------------------------------------
154
155 bool MyApp::OnInit()
156 {
157 // Create the main application window
158 MyFrame *frame = new MyFrame();
159
160 // Show it and tell the application that it's our main window
161 frame->Show(TRUE);
162 SetTopWindow(frame);
163
164 // success
165 return TRUE;
166 }
167
168 // --------------------------------------------------------------------------
169 // main frame
170 // --------------------------------------------------------------------------
171
172 // frame constructor
173 MyFrame::MyFrame() : wxFrame((wxFrame *)NULL, -1,
174 _("wxSocket demo: Client"),
175 wxDefaultPosition, wxSize(300, 200))
176 {
177 // Give the frame an icon
178 SetIcon(wxICON(mondrian));
179
180 // Make menus
181 m_menuFile = new wxMenu();
182 m_menuFile->Append(CLIENT_ABOUT, _("&About...\tCtrl-A"), _("Show about dialog"));
183 m_menuFile->AppendSeparator();
184 m_menuFile->Append(CLIENT_QUIT, _("E&xit\tAlt-X"), _("Quit client"));
185
186 m_menuSocket = new wxMenu();
187 m_menuSocket->Append(CLIENT_OPEN, _("&Open session"), _("Connect to server"));
188 m_menuSocket->AppendSeparator();
189 m_menuSocket->Append(CLIENT_TEST1, _("Test &1"), _("Test basic functionality"));
190 m_menuSocket->Append(CLIENT_TEST2, _("Test &2"), _("Test ReadMsg and WriteMsg"));
191 m_menuSocket->Append(CLIENT_TEST3, _("Test &3"), _("Test large data transfer"));
192 m_menuSocket->AppendSeparator();
193 m_menuSocket->Append(CLIENT_CLOSE, _("&Close session"), _("Close connection"));
194
195 m_menuDatagramSocket = new wxMenu();
196 m_menuDatagramSocket->Append(CLIENT_DGRAM, _("Send Datagram"), _("Test UDP sockets"));
197
198 m_menuProtocols = new wxMenu();
199 m_menuProtocols->Append(CLIENT_TESTURL, _("Test URL"), _("Get data from the specified URL"));
200
201 // Append menus to the menubar
202 m_menuBar = new wxMenuBar();
203 m_menuBar->Append(m_menuFile, _("&File"));
204 m_menuBar->Append(m_menuSocket, _("&SocketClient"));
205 m_menuBar->Append(m_menuDatagramSocket, _("&DatagramSocket"));
206 m_menuBar->Append(m_menuProtocols, _("&Protocols"));
207 SetMenuBar(m_menuBar);
208
209 // Status bar
210 CreateStatusBar(2);
211
212 // Make a panel with a textctrl in it
213 m_panel = new wxPanel(this, -1, wxPoint(0, 0), GetClientSize());
214 m_text = new wxTextCtrl(m_panel, -1,
215 _("Welcome to wxSocket demo: Client\n"
216 "Client ready\n"),
217 wxPoint(0, 0), m_panel->GetClientSize(),
218 wxTE_MULTILINE | wxTE_READONLY);
219
220 // Create the socket
221 m_sock = new wxSocketClient();
222 m_sock->SetEventHandler(*this, SOCKET_ID);
223 m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG |
224 wxSOCKET_INPUT_FLAG |
225 wxSOCKET_LOST_FLAG);
226 m_sock->Notify(TRUE);
227
228 m_busy = FALSE;
229 UpdateStatusBar();
230 }
231
232 MyFrame::~MyFrame()
233 {
234 delete m_sock;
235 }
236
237 // event handlers
238
239 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
240 {
241 // TRUE is to force the frame to close
242 Close(TRUE);
243 }
244
245 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
246 {
247 wxMessageBox(_("wxSocket demo: Client\n"
248 "(c) 1999 Guillermo Rodriguez Garcia\n"),
249 _("About Client"),
250 wxOK | wxICON_INFORMATION, this);
251 }
252
253 void MyFrame::OnOpenConnection(wxCommandEvent& WXUNUSED(event))
254 {
255 wxIPV4address addr;
256
257 m_menuSocket->Enable(CLIENT_OPEN, FALSE);
258 m_menuSocket->Enable(CLIENT_CLOSE, FALSE);
259
260 // Ask user for server address
261 wxString hostname = wxGetTextFromUser(
262 _("Enter the address of the wxSocket demo server:"),
263 _("Connect ..."),
264 _("localhost"));
265
266 addr.Hostname(hostname);
267 addr.Service(3000);
268
269 // Mini-tutorial for Connect() :-)
270 // ---------------------------
271 //
272 // There are two ways to use Connect(): blocking and non-blocking,
273 // depending on the value passed as the 'wait' (2nd) parameter.
274 //
275 // Connect(addr, TRUE) will wait until the connection completes,
276 // returning TRUE on success and FALSE on failure. This call blocks
277 // the GUI (this might be changed in future releases to honour the
278 // wxSOCKET_BLOCK flag).
279 //
280 // Connect(addr, FALSE) will issue a nonblocking connection request
281 // and return immediately. If the return value is TRUE, then the
282 // connection has been already succesfully established. If it is
283 // FALSE, you must wait for the request to complete, either with
284 // WaitOnConnect() or by watching wxSOCKET_CONNECTION / LOST
285 // events (please read the documentation).
286 //
287 // WaitOnConnect() itself never blocks the GUI (this might change
288 // in the future to honour the wxSOCKET_BLOCK flag). This call will
289 // return TRUE if the connection request completed succesfully, or
290 // FALSE otherwise, which in turn might mean:
291 // a) that the specified timeout ellapsed, or
292 // b) that the connection request failed.
293 // Use IsConnected() to distinguish between these two.
294 //
295 // So, in a brief, you should do one of the following things:
296 //
297 // For blocking Connect:
298 //
299 // bool success = Connect(addr, TRUE);
300 //
301 // For nonblocking Connect:
302 //
303 // Connect(addr, FALSE);
304 // WaitOnConnect(seconds, millis);
305 // success = IsConnected();
306 //
307 // And that's all :-)
308 //
309 m_text->AppendText(_("\nTrying to connect (timeout = 10 sec) ...\n"));
310 m_sock->Connect(addr, FALSE);
311 m_sock->WaitOnConnect(10);
312
313 if (m_sock->IsConnected())
314 m_text->AppendText(_("Succeeded ! Connection established\n"));
315 else
316 {
317 m_sock->Close();
318 m_text->AppendText(_("Failed ! Unable to connect\n"));
319 wxMessageBox(_("Can't connect to the specified host"), _("Alert !"));
320 }
321
322 UpdateStatusBar();
323 }
324
325 void MyFrame::OnTest1(wxCommandEvent& WXUNUSED(event))
326 {
327 const char *buf1;
328 char *buf2;
329 unsigned char len;
330
331 // Disable socket menu entries (exception: Close Session)
332 m_busy = TRUE;
333 UpdateStatusBar();
334
335 m_text->AppendText(_("\n=== Test 1 begins ===\n"));
336
337 // Tell the server which test we are running
338 char c = 0xBE;
339 m_sock->Write(&c, 1);
340
341 // Send some data and read it back. We know the size of the
342 // buffer, so we can specify the exact number of bytes to be
343 // sent or received and use the wxSOCKET_WAITALL flag. Also,
344 // we have disabled menu entries which could interfere with
345 // the test, so we can safely avoid the wxSOCKET_BLOCK flag.
346 //
347 // First we send a byte with the length of the string, then
348 // we send the string itself (do NOT try to send any integral
349 // value larger than a byte "as is" across the network, or
350 // you might be in trouble! Ever heard about big and little
351 // endian computers?)
352 //
353 m_sock->SetFlags(wxSOCKET_WAITALL);
354
355 buf1 = _("Test string (less than 256 chars!)");
356 len = wxStrlen(buf1) + 1;
357 buf2 = new char[len];
358
359 m_text->AppendText(_("Sending a test buffer to the server ..."));
360 m_sock->Write((char *)&len, 1);
361 m_sock->Write(buf1, len);
362 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
363
364 m_text->AppendText(_("Receiving the buffer back from server ..."));
365 m_sock->Read(buf2, len);
366 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
367
368 m_text->AppendText(_("Comparing the two buffers ..."));
369 if (memcmp(buf1, buf2, len) != 0)
370 {
371 m_text->AppendText(_("failed!\n"));
372 m_text->AppendText(_("Test 1 failed !\n"));
373 }
374 else
375 {
376 m_text->AppendText(_("done\n"));
377 m_text->AppendText(_("Test 1 passed !\n"));
378 }
379 m_text->AppendText(_("=== Test 1 ends ===\n"));
380
381 delete[] buf2;
382 m_busy = FALSE;
383 UpdateStatusBar();
384 }
385
386 void MyFrame::OnTest2(wxCommandEvent& WXUNUSED(event))
387 {
388 char *msg1;
389 char *msg2;
390 size_t len;
391
392 // Disable socket menu entries (exception: Close Session)
393 m_busy = TRUE;
394 UpdateStatusBar();
395
396 m_text->AppendText(_("\n=== Test 2 begins ===\n"));
397
398 // Tell the server which test we are running
399 char c = 0xCE;
400 m_sock->Write(&c, 1);
401
402 // Here we use ReadMsg and WriteMsg to send messages with
403 // a header with size information. Also, the reception is
404 // event triggered, so we test input events as well.
405 //
406 // We need to set no flags here (ReadMsg and WriteMsg are
407 // not affected by flags)
408 //
409 m_sock->SetFlags(wxSOCKET_WAITALL);
410
411 wxString s = wxGetTextFromUser(
412 _("Enter an arbitrary string to send to the server:"),
413 _("Test 2 ..."),
414 _("Yes I like wxWindows!"));
415
416 msg1 = (char *)s.c_str();
417 len = wxStrlen(msg1) + 1;
418 msg2 = new char[len];
419
420 m_text->AppendText(_("Sending the string with WriteMsg ..."));
421 m_sock->WriteMsg(msg1, len);
422 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
423 m_text->AppendText(_("Waiting for an event (timeout = 2 sec)\n"));
424
425 // Wait until data available (will also return if the connection is lost)
426 m_sock->WaitForRead(2);
427
428 if (m_sock->IsData())
429 {
430 m_text->AppendText(_("Reading the string back with ReadMsg ..."));
431 m_sock->ReadMsg(msg2, len);
432 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
433 m_text->AppendText(_("Comparing the two buffers ..."));
434 if (memcmp(msg1, msg2, len) != 0)
435 {
436 m_text->AppendText(_("failed!\n"));
437 m_text->AppendText(_("Test 2 failed !\n"));
438 }
439 else
440 {
441 m_text->AppendText(_("done\n"));
442 m_text->AppendText(_("Test 2 passed !\n"));
443 }
444 }
445 else
446 m_text->AppendText(_("Timeout ! Test 2 failed.\n"));
447
448 m_text->AppendText(_("=== Test 2 ends ===\n"));
449
450 delete[] msg2;
451 m_busy = FALSE;
452 UpdateStatusBar();
453 }
454
455 void MyFrame::OnTest3(wxCommandEvent& WXUNUSED(event))
456 {
457 char *buf1;
458 char *buf2;
459 unsigned char len;
460
461 // Disable socket menu entries (exception: Close Session)
462 m_busy = TRUE;
463 UpdateStatusBar();
464
465 m_text->AppendText(_("\n=== Test 3 begins ===\n"));
466
467 // Tell the server which test we are running
468 char c = 0xDE;
469 m_sock->Write(&c, 1);
470
471 // This test also is similar to the first one but it sends a
472 // large buffer so that wxSocket is actually forced to split
473 // it into pieces and take care of sending everything before
474 // returning.
475 //
476 m_sock->SetFlags(wxSOCKET_WAITALL);
477
478 // Note that len is in kbytes here!
479 len = 32;
480 buf1 = new char[len * 1024];
481 buf2 = new char[len * 1024];
482
483 for (int i = 0; i < len * 1024; i ++)
484 buf1[i] = (char)(i % 256);
485
486 m_text->AppendText(_("Sending a large buffer (32K) to the server ..."));
487 m_sock->Write((char *)&len, 1);
488 m_sock->Write(buf1, len * 1024);
489 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
490
491 m_text->AppendText(_("Receiving the buffer back from server ..."));
492 m_sock->Read(buf2, len * 1024);
493 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
494
495 m_text->AppendText(_("Comparing the two buffers ..."));
496 if (memcmp(buf1, buf2, len) != 0)
497 {
498 m_text->AppendText(_("failed!\n"));
499 m_text->AppendText(_("Test 3 failed !\n"));
500 }
501 else
502 {
503 m_text->AppendText(_("done\n"));
504 m_text->AppendText(_("Test 3 passed !\n"));
505 }
506 m_text->AppendText(_("=== Test 3 ends ===\n"));
507
508 delete[] buf2;
509 m_busy = FALSE;
510 UpdateStatusBar();
511 }
512
513 void MyFrame::OnCloseConnection(wxCommandEvent& WXUNUSED(event))
514 {
515 m_sock->Close();
516 UpdateStatusBar();
517 }
518
519 void MyFrame::OnDatagram(wxCommandEvent& WXUNUSED(event))
520 {
521 }
522
523 void MyFrame::OnTestURL(wxCommandEvent& WXUNUSED(event))
524 {
525 // Note that we are creating a new socket here, so this
526 // won't mess with the client/server demo.
527
528 // Ask for the URL
529 m_text->AppendText(_("\n=== URL test begins ===\n"));
530 wxString urlname = wxGetTextFromUser(_("Enter an URL to get"),
531 _("URL:"),
532 _("http://localhost"));
533
534 // Parse the URL
535 wxURL url(urlname);
536
537 // Try to get the input stream (connects to the given URL)
538 m_text->AppendText(_("Trying to establish connection...\n"));
539 wxYield();
540 wxInputStream *data = url.GetInputStream();
541 if (!data)
542 {
543 m_text->AppendText(_("Error: couldn't read from URL\n"));
544 m_text->AppendText(_("=== URL test ends ===\n"));
545 return;
546 }
547
548 // Print the contents type and file size
549 wxString s;
550 s.Printf(_("Contents type: %s\n"
551 "File size: %i\n"
552 "Starting to download...\n"),
553 url.GetProtocol().GetContentType().c_str(),
554 data->GetSize());
555 m_text->AppendText(s);
556 wxYield();
557
558 // Get the data
559 wxFileOutputStream sout("test.url");
560 data->Read(sout);
561 m_text->AppendText(_("Results written to file: test.url\n"));
562 m_text->AppendText(_("Done.\n"));
563 m_text->AppendText(_("=== URL test ends ===\n"));
564
565 delete data;
566 }
567
568 void MyFrame::OnSocketEvent(wxSocketEvent& event)
569 {
570 wxString s = _("OnSocketEvent: ");
571
572 switch(event.SocketEvent())
573 {
574 case wxSOCKET_INPUT : s.Append(_("wxSOCKET_INPUT\n")); break;
575 case wxSOCKET_LOST : s.Append(_("wxSOCKET_LOST\n")); break;
576 case wxSOCKET_CONNECTION : s.Append(_("wxSOCKET_CONNECTION\n")); break;
577 default : s.Append(_("Unexpected event !\n")); break;
578 }
579
580 m_text->AppendText(s);
581 UpdateStatusBar();
582 }
583
584 // convenience functions
585
586 void MyFrame::UpdateStatusBar()
587 {
588 wxString s;
589
590 if (!m_sock->IsConnected())
591 {
592 s.Printf(_("Not connected"));
593 }
594 else
595 {
596 wxIPV4address addr;
597
598 m_sock->GetPeer(addr);
599 s.Printf(_("%s : %d"), (addr.Hostname()).c_str(), addr.Service());
600 }
601
602 SetStatusText(s, 1);
603
604 m_menuSocket->Enable(CLIENT_OPEN, !m_sock->IsConnected() && !m_busy);
605 m_menuSocket->Enable(CLIENT_TEST1, m_sock->IsConnected() && !m_busy);
606 m_menuSocket->Enable(CLIENT_TEST2, m_sock->IsConnected() && !m_busy);
607 m_menuSocket->Enable(CLIENT_TEST3, m_sock->IsConnected() && !m_busy);
608 m_menuSocket->Enable(CLIENT_CLOSE, m_sock->IsConnected());
609 }