]> git.saurik.com Git - wxWidgets.git/blob - samples/sockets/client.cpp
fixing incorrect scrolling - which happened at least under OSX ...
[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 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 # pragma hdrstop
25 #endif
26
27 // for all others, include the necessary headers
28 #ifndef WX_PRECOMP
29 # include "wx/wx.h"
30 #endif
31
32 #include "wx/socket.h"
33 #include "wx/url.h"
34 #include "wx/sstream.h"
35 #include <memory>
36
37 // --------------------------------------------------------------------------
38 // resources
39 // --------------------------------------------------------------------------
40
41 // the application icon
42 #ifndef wxHAS_IMAGES_IN_RESOURCES
43 #include "../sample.xpm"
44 #endif
45
46 // --------------------------------------------------------------------------
47 // classes
48 // --------------------------------------------------------------------------
49
50 // Define a new application type
51 class MyApp : public wxApp
52 {
53 public:
54 virtual bool OnInit();
55 };
56
57 // Define a new frame type: this is going to be our main frame
58 class MyFrame : public wxFrame
59 {
60 public:
61 MyFrame();
62 ~MyFrame();
63
64 // event handlers for File menu
65 void OnQuit(wxCommandEvent& event);
66 void OnAbout(wxCommandEvent& event);
67
68 // event handlers for Socket menu
69 void OnOpenConnection(wxCommandEvent& event);
70 void OnTest1(wxCommandEvent& event);
71 void OnTest2(wxCommandEvent& event);
72 void OnTest3(wxCommandEvent& event);
73 void OnCloseConnection(wxCommandEvent& event);
74
75 #if wxUSE_URL
76 // event handlers for Protocols menu
77 void OnTestURL(wxCommandEvent& event);
78 #endif
79 #if wxUSE_IPV6
80 void OnOpenConnectionIPv6(wxCommandEvent& event);
81 #endif
82
83 void OpenConnection(wxSockAddress::Family family);
84
85 // event handlers for DatagramSocket menu (stub)
86 void OnDatagram(wxCommandEvent& event);
87
88 // socket event handler
89 void OnSocketEvent(wxSocketEvent& event);
90
91 // convenience functions
92 void UpdateStatusBar();
93
94 private:
95 wxSocketClient *m_sock;
96 wxTextCtrl *m_text;
97 wxMenu *m_menuFile;
98 wxMenu *m_menuSocket;
99 wxMenu *m_menuDatagramSocket;
100 wxMenu *m_menuProtocols;
101 wxMenuBar *m_menuBar;
102 bool m_busy;
103
104 // any class wishing to process wxWidgets events must use this macro
105 DECLARE_EVENT_TABLE()
106 };
107
108 // simple helper class to log start and end of each test
109 class TestLogger
110 {
111 public:
112 TestLogger(const wxString& name) : m_name(name)
113 {
114 wxLogMessage("=== %s test begins ===", m_name);
115 }
116
117 ~TestLogger()
118 {
119 wxLogMessage("=== %s test ends ===", m_name);
120 }
121
122 private:
123 const wxString m_name;
124 };
125
126 // --------------------------------------------------------------------------
127 // constants
128 // --------------------------------------------------------------------------
129
130 // IDs for the controls and the menu commands
131 enum
132 {
133 // menu items
134 CLIENT_QUIT = wxID_EXIT,
135 CLIENT_ABOUT = wxID_ABOUT,
136 CLIENT_OPEN = 100,
137 #if wxUSE_IPV6
138 CLIENT_OPENIPV6,
139 #endif
140 CLIENT_TEST1,
141 CLIENT_TEST2,
142 CLIENT_TEST3,
143 CLIENT_CLOSE,
144 #if wxUSE_URL
145 CLIENT_TESTURL,
146 #endif
147 CLIENT_DGRAM,
148
149 // id for socket
150 SOCKET_ID
151 };
152
153 // --------------------------------------------------------------------------
154 // event tables and other macros for wxWidgets
155 // --------------------------------------------------------------------------
156
157 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
158 EVT_MENU(CLIENT_QUIT, MyFrame::OnQuit)
159 EVT_MENU(CLIENT_ABOUT, MyFrame::OnAbout)
160 EVT_MENU(CLIENT_OPEN, MyFrame::OnOpenConnection)
161 #if wxUSE_IPV6
162 EVT_MENU(CLIENT_OPENIPV6, MyFrame::OnOpenConnectionIPv6)
163 #endif
164 EVT_MENU(CLIENT_TEST1, MyFrame::OnTest1)
165 EVT_MENU(CLIENT_TEST2, MyFrame::OnTest2)
166 EVT_MENU(CLIENT_TEST3, MyFrame::OnTest3)
167 EVT_MENU(CLIENT_CLOSE, MyFrame::OnCloseConnection)
168 EVT_MENU(CLIENT_DGRAM, MyFrame::OnDatagram)
169 #if wxUSE_URL
170 EVT_MENU(CLIENT_TESTURL, MyFrame::OnTestURL)
171 #endif
172 EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent)
173 END_EVENT_TABLE()
174
175 IMPLEMENT_APP(MyApp)
176
177 // ==========================================================================
178 // implementation
179 // ==========================================================================
180
181 // --------------------------------------------------------------------------
182 // the application class
183 // --------------------------------------------------------------------------
184
185 bool MyApp::OnInit()
186 {
187 if ( !wxApp::OnInit() )
188 return false;
189
190 // Create the main application window
191 MyFrame *frame = new MyFrame();
192
193 // Show it
194 frame->Show(true);
195
196 // success
197 return true;
198 }
199
200 // --------------------------------------------------------------------------
201 // main frame
202 // --------------------------------------------------------------------------
203
204 // frame constructor
205 MyFrame::MyFrame() : wxFrame((wxFrame *)NULL, wxID_ANY,
206 _("wxSocket demo: Client"),
207 wxDefaultPosition, wxSize(300, 200))
208 {
209 // Give the frame an icon
210 SetIcon(wxICON(sample));
211
212 // Make menus
213 m_menuFile = new wxMenu();
214 m_menuFile->Append(CLIENT_ABOUT, _("&About\tCtrl-A"), _("Show about dialog"));
215 m_menuFile->AppendSeparator();
216 m_menuFile->Append(CLIENT_QUIT, _("E&xit\tAlt-X"), _("Quit client"));
217
218 m_menuSocket = new wxMenu();
219 m_menuSocket->Append(CLIENT_OPEN, _("&Open session\tCtrl-O"), _("Connect to server"));
220 #if wxUSE_IPV6
221 m_menuSocket->Append(CLIENT_OPENIPV6, _("&Open session(IPv6)\tShift-Ctrl-O"), _("Connect to server(IPv6)"));
222 #endif
223 m_menuSocket->AppendSeparator();
224 m_menuSocket->Append(CLIENT_TEST1, _("Test &1\tCtrl-F1"), _("Test basic functionality"));
225 m_menuSocket->Append(CLIENT_TEST2, _("Test &2\tCtrl-F2"), _("Test ReadMsg and WriteMsg"));
226 m_menuSocket->Append(CLIENT_TEST3, _("Test &3\tCtrl-F3"), _("Test large data transfer"));
227 m_menuSocket->AppendSeparator();
228 m_menuSocket->Append(CLIENT_CLOSE, _("&Close session\tCtrl-Q"), _("Close connection"));
229
230 m_menuDatagramSocket = new wxMenu();
231 m_menuDatagramSocket->Append(CLIENT_DGRAM, _("&Datagram test\tCtrl-D"), _("Test UDP sockets"));
232
233 #if wxUSE_URL
234 m_menuProtocols = new wxMenu();
235 m_menuProtocols->Append(CLIENT_TESTURL, _("Test URL\tCtrl-U"),
236 _("Get data from the specified URL"));
237 #endif
238
239 // Append menus to the menubar
240 m_menuBar = new wxMenuBar();
241 m_menuBar->Append(m_menuFile, _("&File"));
242 m_menuBar->Append(m_menuSocket, _("&TCP"));
243 m_menuBar->Append(m_menuDatagramSocket, _("&UDP"));
244 #if wxUSE_URL
245 m_menuBar->Append(m_menuProtocols, _("&Protocols"));
246 #endif
247 SetMenuBar(m_menuBar);
248
249 #if wxUSE_STATUSBAR
250 // Status bar
251 CreateStatusBar(2);
252 #endif // wxUSE_STATUSBAR
253
254 // Make a textctrl for logging
255 m_text = new wxTextCtrl(this, wxID_ANY,
256 _("Welcome to wxSocket demo: Client\nClient ready\n"),
257 wxDefaultPosition, wxDefaultSize,
258 wxTE_MULTILINE | wxTE_READONLY);
259 delete wxLog::SetActiveTarget(new wxLogTextCtrl(m_text));
260
261 // Create the socket
262 m_sock = new wxSocketClient();
263
264 // Setup the event handler and subscribe to most events
265 m_sock->SetEventHandler(*this, SOCKET_ID);
266 m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG |
267 wxSOCKET_INPUT_FLAG |
268 wxSOCKET_LOST_FLAG);
269 m_sock->Notify(true);
270
271 m_busy = false;
272 UpdateStatusBar();
273 }
274
275 MyFrame::~MyFrame()
276 {
277 // No delayed deletion here, as the frame is dying anyway
278 delete m_sock;
279 }
280
281 // event handlers
282
283 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
284 {
285 // true is to force the frame to close
286 Close(true);
287 }
288
289 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
290 {
291 wxMessageBox(_("wxSocket demo: Client\n(c) 1999 Guillermo Rodriguez Garcia\n"),
292 _("About Client"),
293 wxOK | wxICON_INFORMATION, this);
294 }
295
296 void MyFrame::OnOpenConnection(wxCommandEvent& WXUNUSED(event))
297 {
298 OpenConnection(wxSockAddress::IPV4);
299 }
300 #if wxUSE_IPV6
301 void MyFrame::OnOpenConnectionIPv6(wxCommandEvent& WXUNUSED(event))
302 {
303 OpenConnection(wxSockAddress::IPV6);
304 }
305 #endif // wxUSE_IPV6
306
307 void MyFrame::OpenConnection(wxSockAddress::Family family)
308 {
309 wxUnusedVar(family); // unused in !wxUSE_IPV6 case
310
311 wxIPaddress * addr;
312 wxIPV4address addr4;
313 #if wxUSE_IPV6
314 wxIPV6address addr6;
315 if ( family == wxSockAddress::IPV6 )
316 addr = &addr6;
317 else
318 #endif
319 addr = &addr4;
320
321 m_menuSocket->Enable(CLIENT_OPEN, false);
322 #if wxUSE_IPV6
323 m_menuSocket->Enable(CLIENT_OPENIPV6, false);
324 #endif
325 m_menuSocket->Enable(CLIENT_CLOSE, false);
326
327 // Ask user for server address
328 wxString hostname = wxGetTextFromUser(
329 _("Enter the address of the wxSocket demo server:"),
330 _("Connect ..."),
331 _("localhost"));
332 if ( hostname.empty() )
333 return;
334
335 addr->Hostname(hostname);
336 addr->Service(3000);
337
338 // we connect asynchronously and will get a wxSOCKET_CONNECTION event when
339 // the connection is really established
340 //
341 // if you want to make sure that connection is established right here you
342 // could call WaitOnConnect(timeout) instead
343 wxLogMessage("Trying to connect to %s:%d", hostname, addr->Service());
344
345 m_sock->Connect(*addr, false);
346 }
347
348 void MyFrame::OnTest1(wxCommandEvent& WXUNUSED(event))
349 {
350 // Disable socket menu entries (exception: Close Session)
351 m_busy = true;
352 UpdateStatusBar();
353
354 m_text->AppendText(_("\n=== Test 1 begins ===\n"));
355
356 // Tell the server which test we are running
357 unsigned char c = 0xBE;
358 m_sock->Write(&c, 1);
359
360 // Send some data and read it back. We know the size of the
361 // buffer, so we can specify the exact number of bytes to be
362 // sent or received and use the wxSOCKET_WAITALL flag. Also,
363 // we have disabled menu entries which could interfere with
364 // the test, so we can safely avoid the wxSOCKET_BLOCK flag.
365 //
366 // First we send a byte with the length of the string, then
367 // we send the string itself (do NOT try to send any integral
368 // value larger than a byte "as is" across the network, or
369 // you might be in trouble! Ever heard about big and little
370 // endian computers?)
371
372 m_sock->SetFlags(wxSOCKET_WAITALL);
373
374 const char *buf1 = "Test string (less than 256 chars!)";
375 unsigned char len = (unsigned char)(wxStrlen(buf1) + 1);
376 wxCharBuffer buf2(wxStrlen(buf1));
377
378 m_text->AppendText(_("Sending a test buffer to the server ..."));
379 m_sock->Write(&len, 1);
380 m_sock->Write(buf1, len);
381 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
382
383 m_text->AppendText(_("Receiving the buffer back from server ..."));
384 m_sock->Read(buf2.data(), len);
385 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
386
387 m_text->AppendText(_("Comparing the two buffers ..."));
388 if (memcmp(buf1, buf2, len) != 0)
389 {
390 m_text->AppendText(_("failed!\n"));
391 m_text->AppendText(_("Test 1 failed !\n"));
392 }
393 else
394 {
395 m_text->AppendText(_("done\n"));
396 m_text->AppendText(_("Test 1 passed !\n"));
397 }
398 m_text->AppendText(_("=== Test 1 ends ===\n"));
399
400 m_busy = false;
401 UpdateStatusBar();
402 }
403
404 void MyFrame::OnTest2(wxCommandEvent& WXUNUSED(event))
405 {
406 // Disable socket menu entries (exception: Close Session)
407 m_busy = true;
408 UpdateStatusBar();
409
410 m_text->AppendText(_("\n=== Test 2 begins ===\n"));
411
412 // Tell the server which test we are running
413 unsigned char c = 0xCE;
414 m_sock->Write(&c, 1);
415
416 // Here we use ReadMsg and WriteMsg to send messages with
417 // a header with size information. Also, the reception is
418 // event triggered, so we test input events as well.
419 //
420 // We need to set no flags here (ReadMsg and WriteMsg are
421 // not affected by flags)
422
423 m_sock->SetFlags(wxSOCKET_WAITALL);
424
425 wxString s = wxGetTextFromUser(
426 _("Enter an arbitrary string to send to the server:"),
427 _("Test 2 ..."),
428 _("Yes I like wxWidgets!"));
429
430 const wxScopedCharBuffer msg1(s.utf8_str());
431 size_t len = wxStrlen(msg1) + 1;
432 wxCharBuffer msg2(wxStrlen(msg1));
433
434 m_text->AppendText(_("Sending the string with WriteMsg ..."));
435 m_sock->WriteMsg(msg1, len);
436 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
437 m_text->AppendText(_("Waiting for an event (timeout = 2 sec)\n"));
438
439 // Wait until data available (will also return if the connection is lost)
440 m_sock->WaitForRead(2);
441
442 if (m_sock->IsData())
443 {
444 m_text->AppendText(_("Reading the string back with ReadMsg ..."));
445 m_sock->ReadMsg(msg2.data(), len);
446 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
447 m_text->AppendText(_("Comparing the two buffers ..."));
448 if (memcmp(msg1, msg2, len) != 0)
449 {
450 m_text->AppendText(_("failed!\n"));
451 m_text->AppendText(_("Test 2 failed !\n"));
452 }
453 else
454 {
455 m_text->AppendText(_("done\n"));
456 m_text->AppendText(_("Test 2 passed !\n"));
457 }
458 }
459 else
460 m_text->AppendText(_("Timeout ! Test 2 failed.\n"));
461
462 m_text->AppendText(_("=== Test 2 ends ===\n"));
463
464 m_busy = false;
465 UpdateStatusBar();
466 }
467
468 void MyFrame::OnTest3(wxCommandEvent& WXUNUSED(event))
469 {
470 // Disable socket menu entries (exception: Close Session)
471 m_busy = true;
472 UpdateStatusBar();
473
474 m_text->AppendText(_("\n=== Test 3 begins ===\n"));
475
476 // Tell the server which test we are running
477 unsigned char c = 0xDE;
478 m_sock->Write(&c, 1);
479
480 // This test also is similar to the first one but it sends a
481 // large buffer so that wxSocket is actually forced to split
482 // it into pieces and take care of sending everything before
483 // returning.
484
485 m_sock->SetFlags(wxSOCKET_WAITALL);
486
487 // Note that len is in kbytes here!
488 const unsigned char len = 32;
489 wxCharBuffer buf1(len * 1024),
490 buf2(len * 1024);
491
492 for (size_t i = 0; i < len * 1024; i ++)
493 buf1.data()[i] = (char)(i % 256);
494
495 m_text->AppendText(_("Sending a large buffer (32K) to the server ..."));
496 m_sock->Write(&len, 1);
497 m_sock->Write(buf1, len * 1024);
498 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
499
500 m_text->AppendText(_("Receiving the buffer back from server ..."));
501 m_sock->Read(buf2.data(), len * 1024);
502 m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
503
504 m_text->AppendText(_("Comparing the two buffers ..."));
505 if (memcmp(buf1, buf2, len) != 0)
506 {
507 m_text->AppendText(_("failed!\n"));
508 m_text->AppendText(_("Test 3 failed !\n"));
509 }
510 else
511 {
512 m_text->AppendText(_("done\n"));
513 m_text->AppendText(_("Test 3 passed !\n"));
514 }
515 m_text->AppendText(_("=== Test 3 ends ===\n"));
516
517 m_busy = false;
518 UpdateStatusBar();
519 }
520
521 void MyFrame::OnCloseConnection(wxCommandEvent& WXUNUSED(event))
522 {
523 m_sock->Close();
524 UpdateStatusBar();
525 }
526
527 void MyFrame::OnDatagram(wxCommandEvent& WXUNUSED(event))
528 {
529 wxString hostname = wxGetTextFromUser
530 (
531 "Enter the address of the wxSocket demo server:",
532 "UDP peer",
533 "localhost"
534 );
535 if ( hostname.empty() )
536 return;
537
538 TestLogger logtest("UDP");
539
540 wxIPV4address addrLocal;
541 addrLocal.Hostname();
542 wxDatagramSocket sock(addrLocal);
543 if ( !sock.IsOk() )
544 {
545 wxLogMessage("ERROR: failed to create UDP socket");
546 return;
547 }
548
549 wxLogMessage("Created UDP socket at %s:%u",
550 addrLocal.IPAddress(), addrLocal.Service());
551
552 wxIPV4address addrPeer;
553 addrPeer.Hostname(hostname);
554 addrPeer.Service(3000);
555
556 wxLogMessage("Testing UDP with peer at %s:%u",
557 addrPeer.IPAddress(), addrPeer.Service());
558
559 char buf[] = "Uryyb sebz pyvrag!";
560 if ( sock.SendTo(addrPeer, buf, sizeof(buf)).LastCount() != sizeof(buf) )
561 {
562 wxLogMessage("ERROR: failed to send data");
563 return;
564 }
565
566 if ( sock.RecvFrom(addrPeer, buf, sizeof(buf)).LastCount() != sizeof(buf) )
567 {
568 wxLogMessage("ERROR: failed to receive data");
569 return;
570 }
571
572 wxLogMessage("Received \"%s\" from %s:%u.",
573 wxString::From8BitData(buf, sock.LastCount()),
574 addrPeer.IPAddress(), addrPeer.Service());
575 }
576
577 #if wxUSE_URL
578
579 void MyFrame::OnTestURL(wxCommandEvent& WXUNUSED(event))
580 {
581 // Ask for the URL
582 static wxString s_urlname("http://www.google.com/");
583 wxString urlname = wxGetTextFromUser
584 (
585 _("Enter an URL to get"),
586 _("URL:"),
587 s_urlname
588 );
589 if ( urlname.empty() )
590 return; // cancelled by user
591
592 s_urlname = urlname;
593
594
595 TestLogger logtest("URL");
596
597 // Parse the URL
598 wxURL url(urlname);
599 if ( url.GetError() != wxURL_NOERR )
600 {
601 wxLogError("Failed to parse URL \"%s\"", urlname);
602 return;
603 }
604
605 // Try to get the input stream (connects to the given URL)
606 wxLogMessage("Establishing connection to \"%s\"...", urlname);
607 const std::auto_ptr<wxInputStream> data(url.GetInputStream());
608 if ( !data.get() )
609 {
610 wxLogError("Failed to retrieve URL \"%s\"", urlname);
611 return;
612 }
613
614 // Print the contents type and file size
615 wxLogMessage("Contents type: %s\nFile size: %lu\nStarting to download...",
616 url.GetProtocol().GetContentType(),
617 static_cast<unsigned long>( data->GetSize() ));
618
619 // Get the data
620 wxStringOutputStream sout;
621 if ( data->Read(sout).GetLastError() != wxSTREAM_EOF )
622 {
623 wxLogError("Error reading the input stream.");
624 }
625
626 wxLogMessage("Text retrieved from URL \"%s\" follows:\n%s",
627 urlname, sout.GetString());
628 }
629
630 #endif // wxUSE_URL
631
632 void MyFrame::OnSocketEvent(wxSocketEvent& event)
633 {
634 switch ( event.GetSocketEvent() )
635 {
636 case wxSOCKET_INPUT:
637 wxLogMessage("Input available on the socket");
638 break;
639
640 case wxSOCKET_LOST:
641 wxLogMessage("Socket connection was unexpectedly lost.");
642 UpdateStatusBar();
643 break;
644
645 case wxSOCKET_CONNECTION:
646 wxLogMessage("... socket is now connected.");
647 UpdateStatusBar();
648 break;
649
650 default:
651 wxLogMessage("Unknown socket event!!!");
652 break;
653 }
654 }
655
656 // convenience functions
657
658 void MyFrame::UpdateStatusBar()
659 {
660 #if wxUSE_STATUSBAR
661 wxString s;
662
663 if (!m_sock->IsConnected())
664 {
665 s = "Not connected";
666 }
667 else
668 {
669 #if wxUSE_IPV6
670 wxIPV6address addr;
671 #else
672 wxIPV4address addr;
673 #endif
674
675 m_sock->GetPeer(addr);
676 s.Printf("%s : %d", addr.Hostname(), addr.Service());
677 }
678
679 SetStatusText(s, 1);
680 #endif // wxUSE_STATUSBAR
681
682 m_menuSocket->Enable(CLIENT_OPEN, !m_sock->IsConnected() && !m_busy);
683 #if wxUSE_IPV6
684 m_menuSocket->Enable(CLIENT_OPENIPV6, !m_sock->IsConnected() && !m_busy);
685 #endif
686 m_menuSocket->Enable(CLIENT_TEST1, m_sock->IsConnected() && !m_busy);
687 m_menuSocket->Enable(CLIENT_TEST2, m_sock->IsConnected() && !m_busy);
688 m_menuSocket->Enable(CLIENT_TEST3, m_sock->IsConnected() && !m_busy);
689 m_menuSocket->Enable(CLIENT_CLOSE, m_sock->IsConnected());
690 }