]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: server.cpp | |
3 | // Purpose: Server for wxSocket demo | |
4 | // Author: Guillermo Rodriguez Garcia <guille@iies.es> | |
5 | // Created: 1999/09/19 | |
6 | // RCS-ID: $Id$ | |
7 | // Copyright: (c) 1999 Guillermo Rodriguez Garcia | |
8 | // (c) 2009 Vadim Zeitlin | |
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/busyinfo.h" | |
33 | #include "wx/socket.h" | |
34 | ||
35 | // this example is currently written to use only IP or only IPv6 sockets, it | |
36 | // should be extended to allow using either in the future | |
37 | #if wxUSE_IPV6 | |
38 | typedef wxIPV6address IPaddress; | |
39 | #else | |
40 | typedef wxIPV4address IPaddress; | |
41 | #endif | |
42 | ||
43 | // -------------------------------------------------------------------------- | |
44 | // resources | |
45 | // -------------------------------------------------------------------------- | |
46 | ||
47 | // the application icon | |
48 | #if !defined(__WXMSW__) && !defined(__WXPM__) | |
49 | #include "../sample.xpm" | |
50 | #endif | |
51 | ||
52 | // -------------------------------------------------------------------------- | |
53 | // classes | |
54 | // -------------------------------------------------------------------------- | |
55 | ||
56 | // Define a new application type | |
57 | class MyApp : public wxApp | |
58 | { | |
59 | public: | |
60 | virtual bool OnInit(); | |
61 | }; | |
62 | ||
63 | // Define a new frame type: this is going to be our main frame | |
64 | class MyFrame : public wxFrame | |
65 | { | |
66 | public: | |
67 | MyFrame(); | |
68 | ~MyFrame(); | |
69 | ||
70 | // event handlers (these functions should _not_ be virtual) | |
71 | void OnUDPTest(wxCommandEvent& event); | |
72 | void OnWaitForAccept(wxCommandEvent& event); | |
73 | void OnQuit(wxCommandEvent& event); | |
74 | void OnAbout(wxCommandEvent& event); | |
75 | void OnServerEvent(wxSocketEvent& event); | |
76 | void OnSocketEvent(wxSocketEvent& event); | |
77 | ||
78 | void Test1(wxSocketBase *sock); | |
79 | void Test2(wxSocketBase *sock); | |
80 | void Test3(wxSocketBase *sock); | |
81 | ||
82 | // convenience functions | |
83 | void UpdateStatusBar(); | |
84 | ||
85 | private: | |
86 | wxSocketServer *m_server; | |
87 | wxTextCtrl *m_text; | |
88 | wxMenu *m_menuFile; | |
89 | wxMenuBar *m_menuBar; | |
90 | bool m_busy; | |
91 | int m_numClients; | |
92 | ||
93 | // any class wishing to process wxWidgets events must use this macro | |
94 | DECLARE_EVENT_TABLE() | |
95 | }; | |
96 | ||
97 | // simple helper class to log start and end of each test | |
98 | class TestLogger | |
99 | { | |
100 | public: | |
101 | TestLogger(const wxString& name) : m_name(name) | |
102 | { | |
103 | wxLogMessage("=== %s begins ===", m_name); | |
104 | } | |
105 | ||
106 | ~TestLogger() | |
107 | { | |
108 | wxLogMessage("=== %s ends ===", m_name); | |
109 | } | |
110 | ||
111 | private: | |
112 | const wxString m_name; | |
113 | }; | |
114 | ||
115 | // -------------------------------------------------------------------------- | |
116 | // constants | |
117 | // -------------------------------------------------------------------------- | |
118 | ||
119 | // IDs for the controls and the menu commands | |
120 | enum | |
121 | { | |
122 | // menu items | |
123 | SERVER_UDPTEST = 10, | |
124 | SERVER_WAITFORACCEPT, | |
125 | SERVER_QUIT = wxID_EXIT, | |
126 | SERVER_ABOUT = wxID_ABOUT, | |
127 | ||
128 | // id for sockets | |
129 | SERVER_ID = 100, | |
130 | SOCKET_ID | |
131 | }; | |
132 | ||
133 | // -------------------------------------------------------------------------- | |
134 | // event tables and other macros for wxWidgets | |
135 | // -------------------------------------------------------------------------- | |
136 | ||
137 | BEGIN_EVENT_TABLE(MyFrame, wxFrame) | |
138 | EVT_MENU(SERVER_QUIT, MyFrame::OnQuit) | |
139 | EVT_MENU(SERVER_ABOUT, MyFrame::OnAbout) | |
140 | EVT_MENU(SERVER_UDPTEST, MyFrame::OnUDPTest) | |
141 | EVT_MENU(SERVER_WAITFORACCEPT, MyFrame::OnWaitForAccept) | |
142 | EVT_SOCKET(SERVER_ID, MyFrame::OnServerEvent) | |
143 | EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent) | |
144 | END_EVENT_TABLE() | |
145 | ||
146 | IMPLEMENT_APP(MyApp) | |
147 | ||
148 | ||
149 | // ========================================================================== | |
150 | // implementation | |
151 | // ========================================================================== | |
152 | ||
153 | // -------------------------------------------------------------------------- | |
154 | // the application class | |
155 | // -------------------------------------------------------------------------- | |
156 | ||
157 | bool MyApp::OnInit() | |
158 | { | |
159 | if ( !wxApp::OnInit() ) | |
160 | return false; | |
161 | ||
162 | // Create the main application window | |
163 | MyFrame *frame = new MyFrame(); | |
164 | ||
165 | // Show it and tell the application that it's our main window | |
166 | frame->Show(true); | |
167 | SetTopWindow(frame); | |
168 | ||
169 | // Success | |
170 | return true; | |
171 | } | |
172 | ||
173 | // -------------------------------------------------------------------------- | |
174 | // main frame | |
175 | // -------------------------------------------------------------------------- | |
176 | ||
177 | // frame constructor | |
178 | ||
179 | MyFrame::MyFrame() : wxFrame((wxFrame *)NULL, wxID_ANY, | |
180 | _("wxSocket demo: Server"), | |
181 | wxDefaultPosition, wxSize(300, 200)) | |
182 | { | |
183 | // Give the frame an icon | |
184 | SetIcon(wxICON(sample)); | |
185 | ||
186 | // Make menus | |
187 | m_menuFile = new wxMenu(); | |
188 | m_menuFile->Append(SERVER_WAITFORACCEPT, "&Wait for connection\tCtrl-W"); | |
189 | m_menuFile->Append(SERVER_UDPTEST, "&UDP test\tCtrl-U"); | |
190 | m_menuFile->AppendSeparator(); | |
191 | m_menuFile->Append(SERVER_ABOUT, _("&About...\tCtrl-A"), _("Show about dialog")); | |
192 | m_menuFile->AppendSeparator(); | |
193 | m_menuFile->Append(SERVER_QUIT, _("E&xit\tAlt-X"), _("Quit server")); | |
194 | ||
195 | // Append menus to the menubar | |
196 | m_menuBar = new wxMenuBar(); | |
197 | m_menuBar->Append(m_menuFile, _("&File")); | |
198 | SetMenuBar(m_menuBar); | |
199 | ||
200 | #if wxUSE_STATUSBAR | |
201 | // Status bar | |
202 | CreateStatusBar(2); | |
203 | #endif // wxUSE_STATUSBAR | |
204 | ||
205 | // Make a textctrl for logging | |
206 | m_text = new wxTextCtrl(this, wxID_ANY, | |
207 | _("Welcome to wxSocket demo: Server\n"), | |
208 | wxDefaultPosition, wxDefaultSize, | |
209 | wxTE_MULTILINE | wxTE_READONLY); | |
210 | delete wxLog::SetActiveTarget(new wxLogTextCtrl(m_text)); | |
211 | ||
212 | // Create the address - defaults to localhost:0 initially | |
213 | IPaddress addr; | |
214 | addr.Service(3000); | |
215 | ||
216 | wxLogMessage("Creating server at %s:%u", addr.IPAddress(), addr.Service()); | |
217 | ||
218 | // Create the socket | |
219 | m_server = new wxSocketServer(addr); | |
220 | ||
221 | // We use Ok() here to see if the server is really listening | |
222 | if (! m_server->Ok()) | |
223 | { | |
224 | wxLogMessage("Could not listen at the specified port !"); | |
225 | return; | |
226 | } | |
227 | ||
228 | IPaddress addrReal; | |
229 | if ( !m_server->GetLocal(addrReal) ) | |
230 | { | |
231 | wxLogMessage("ERROR: couldn't get the address we bound to"); | |
232 | } | |
233 | else | |
234 | { | |
235 | wxLogMessage("Server listening at %s:%u", | |
236 | addrReal.IPAddress(), addrReal.Service()); | |
237 | } | |
238 | ||
239 | // Setup the event handler and subscribe to connection events | |
240 | m_server->SetEventHandler(*this, SERVER_ID); | |
241 | m_server->SetNotify(wxSOCKET_CONNECTION_FLAG); | |
242 | m_server->Notify(true); | |
243 | ||
244 | m_busy = false; | |
245 | m_numClients = 0; | |
246 | UpdateStatusBar(); | |
247 | } | |
248 | ||
249 | MyFrame::~MyFrame() | |
250 | { | |
251 | // No delayed deletion here, as the frame is dying anyway | |
252 | delete m_server; | |
253 | } | |
254 | ||
255 | // event handlers | |
256 | ||
257 | void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) | |
258 | { | |
259 | // true is to force the frame to close | |
260 | Close(true); | |
261 | } | |
262 | ||
263 | void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) | |
264 | { | |
265 | wxMessageBox(_("wxSocket demo: Server\n(c) 1999 Guillermo Rodriguez Garcia\n"), | |
266 | _("About Server"), | |
267 | wxOK | wxICON_INFORMATION, this); | |
268 | } | |
269 | ||
270 | void MyFrame::OnUDPTest(wxCommandEvent& WXUNUSED(event)) | |
271 | { | |
272 | TestLogger logtest("UDP test"); | |
273 | ||
274 | IPaddress addr; | |
275 | addr.Service(3000); | |
276 | wxDatagramSocket sock(addr); | |
277 | ||
278 | char buf[1024]; | |
279 | size_t n = sock.RecvFrom(addr, buf, sizeof(buf)).LastCount(); | |
280 | if ( !n ) | |
281 | { | |
282 | wxLogMessage("ERROR: failed to receive data"); | |
283 | return; | |
284 | } | |
285 | ||
286 | wxLogMessage("Received \"%s\" from %s:%u.", | |
287 | wxString::From8BitData(buf, n), | |
288 | addr.IPAddress(), addr.Service()); | |
289 | ||
290 | for ( size_t i = 0; i < n; i++ ) | |
291 | { | |
292 | char& c = buf[i]; | |
293 | if ( (c >= 'A' && c <= 'M') || (c >= 'a' && c <= 'm') ) | |
294 | c += 13; | |
295 | else if ( (c >= 'N' && c <= 'Z') || (c >= 'n' && c <= 'z') ) | |
296 | c -= 13; | |
297 | } | |
298 | ||
299 | if ( sock.SendTo(addr, buf, n).LastCount() != n ) | |
300 | { | |
301 | wxLogMessage("ERROR: failed to send data"); | |
302 | return; | |
303 | } | |
304 | } | |
305 | ||
306 | void MyFrame::OnWaitForAccept(wxCommandEvent& WXUNUSED(event)) | |
307 | { | |
308 | TestLogger logtest("WaitForAccept() test"); | |
309 | ||
310 | wxBusyInfo("Waiting for connection for 10 seconds...", this); | |
311 | if ( m_server->WaitForAccept(10) ) | |
312 | wxLogMessage("Accepted client connection."); | |
313 | else | |
314 | wxLogMessage("Connection error or timeout expired."); | |
315 | } | |
316 | ||
317 | void MyFrame::Test1(wxSocketBase *sock) | |
318 | { | |
319 | TestLogger logtest("Test 1"); | |
320 | ||
321 | // Receive data from socket and send it back. We will first | |
322 | // get a byte with the buffer size, so we can specify the | |
323 | // exact size and use the wxSOCKET_WAITALL flag. Also, we | |
324 | // disabled input events so we won't have unwanted reentrance. | |
325 | // This way we can avoid the infamous wxSOCKET_BLOCK flag. | |
326 | ||
327 | sock->SetFlags(wxSOCKET_WAITALL); | |
328 | ||
329 | // Read the size | |
330 | unsigned char len; | |
331 | sock->Read(&len, 1); | |
332 | wxCharBuffer buf(len); | |
333 | ||
334 | // Read the data | |
335 | sock->Read(buf.data(), len); | |
336 | wxLogMessage("Got the data, sending it back"); | |
337 | ||
338 | // Write it back | |
339 | sock->Write(buf, len); | |
340 | } | |
341 | ||
342 | void MyFrame::Test2(wxSocketBase *sock) | |
343 | { | |
344 | char buf[4096]; | |
345 | ||
346 | TestLogger logtest("Test 2"); | |
347 | ||
348 | // We don't need to set flags because ReadMsg and WriteMsg | |
349 | // are not affected by them anyway. | |
350 | ||
351 | // Read the message | |
352 | wxUint32 len = sock->ReadMsg(buf, sizeof(buf)).LastCount(); | |
353 | if ( !len ) | |
354 | { | |
355 | wxLogError("Failed to read message."); | |
356 | return; | |
357 | } | |
358 | ||
359 | wxLogMessage("Got \"%s\" from client.", wxString::FromUTF8(buf, len)); | |
360 | wxLogMessage("Sending the data back"); | |
361 | ||
362 | // Write it back | |
363 | sock->WriteMsg(buf, len); | |
364 | } | |
365 | ||
366 | void MyFrame::Test3(wxSocketBase *sock) | |
367 | { | |
368 | TestLogger logtest("Test 3"); | |
369 | ||
370 | // This test is similar to the first one, but the len is | |
371 | // expressed in kbytes - this tests large data transfers. | |
372 | ||
373 | sock->SetFlags(wxSOCKET_WAITALL); | |
374 | ||
375 | // Read the size | |
376 | unsigned char len; | |
377 | sock->Read(&len, 1); | |
378 | wxCharBuffer buf(len*1024); | |
379 | ||
380 | // Read the data | |
381 | sock->Read(buf.data(), len * 1024); | |
382 | wxLogMessage("Got the data, sending it back"); | |
383 | ||
384 | // Write it back | |
385 | sock->Write(buf, len * 1024); | |
386 | } | |
387 | ||
388 | void MyFrame::OnServerEvent(wxSocketEvent& event) | |
389 | { | |
390 | wxString s = _("OnServerEvent: "); | |
391 | wxSocketBase *sock; | |
392 | ||
393 | switch(event.GetSocketEvent()) | |
394 | { | |
395 | case wxSOCKET_CONNECTION : s.Append(_("wxSOCKET_CONNECTION\n")); break; | |
396 | default : s.Append(_("Unexpected event !\n")); break; | |
397 | } | |
398 | ||
399 | m_text->AppendText(s); | |
400 | ||
401 | // Accept new connection if there is one in the pending | |
402 | // connections queue, else exit. We use Accept(false) for | |
403 | // non-blocking accept (although if we got here, there | |
404 | // should ALWAYS be a pending connection). | |
405 | ||
406 | sock = m_server->Accept(false); | |
407 | ||
408 | if (sock) | |
409 | { | |
410 | IPaddress addr; | |
411 | if ( !sock->GetPeer(addr) ) | |
412 | { | |
413 | wxLogMessage("New connection from unknown client accepted."); | |
414 | } | |
415 | else | |
416 | { | |
417 | wxLogMessage("New client connection from %s:%u accepted", | |
418 | addr.IPAddress(), addr.Service()); | |
419 | } | |
420 | } | |
421 | else | |
422 | { | |
423 | wxLogMessage("Error: couldn't accept a new connection"); | |
424 | return; | |
425 | } | |
426 | ||
427 | sock->SetEventHandler(*this, SOCKET_ID); | |
428 | sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG); | |
429 | sock->Notify(true); | |
430 | ||
431 | m_numClients++; | |
432 | UpdateStatusBar(); | |
433 | } | |
434 | ||
435 | void MyFrame::OnSocketEvent(wxSocketEvent& event) | |
436 | { | |
437 | wxString s = _("OnSocketEvent: "); | |
438 | wxSocketBase *sock = event.GetSocket(); | |
439 | ||
440 | // First, print a message | |
441 | switch(event.GetSocketEvent()) | |
442 | { | |
443 | case wxSOCKET_INPUT : s.Append(_("wxSOCKET_INPUT\n")); break; | |
444 | case wxSOCKET_LOST : s.Append(_("wxSOCKET_LOST\n")); break; | |
445 | default : s.Append(_("Unexpected event !\n")); break; | |
446 | } | |
447 | ||
448 | m_text->AppendText(s); | |
449 | ||
450 | // Now we process the event | |
451 | switch(event.GetSocketEvent()) | |
452 | { | |
453 | case wxSOCKET_INPUT: | |
454 | { | |
455 | // We disable input events, so that the test doesn't trigger | |
456 | // wxSocketEvent again. | |
457 | sock->SetNotify(wxSOCKET_LOST_FLAG); | |
458 | ||
459 | // Which test are we going to run? | |
460 | unsigned char c; | |
461 | sock->Read(&c, 1); | |
462 | ||
463 | switch (c) | |
464 | { | |
465 | case 0xBE: Test1(sock); break; | |
466 | case 0xCE: Test2(sock); break; | |
467 | case 0xDE: Test3(sock); break; | |
468 | default: | |
469 | wxLogMessage("Unknown test id received from client"); | |
470 | } | |
471 | ||
472 | // Enable input events again. | |
473 | sock->SetNotify(wxSOCKET_LOST_FLAG | wxSOCKET_INPUT_FLAG); | |
474 | break; | |
475 | } | |
476 | case wxSOCKET_LOST: | |
477 | { | |
478 | m_numClients--; | |
479 | ||
480 | // Destroy() should be used instead of delete wherever possible, | |
481 | // due to the fact that wxSocket uses 'delayed events' (see the | |
482 | // documentation for wxPostEvent) and we don't want an event to | |
483 | // arrive to the event handler (the frame, here) after the socket | |
484 | // has been deleted. Also, we might be doing some other thing with | |
485 | // the socket at the same time; for example, we might be in the | |
486 | // middle of a test or something. Destroy() takes care of all | |
487 | // this for us. | |
488 | ||
489 | wxLogMessage("Deleting socket."); | |
490 | sock->Destroy(); | |
491 | break; | |
492 | } | |
493 | default: ; | |
494 | } | |
495 | ||
496 | UpdateStatusBar(); | |
497 | } | |
498 | ||
499 | // convenience functions | |
500 | ||
501 | void MyFrame::UpdateStatusBar() | |
502 | { | |
503 | #if wxUSE_STATUSBAR | |
504 | wxString s; | |
505 | s.Printf(_("%d clients connected"), m_numClients); | |
506 | SetStatusText(s, 1); | |
507 | #endif // wxUSE_STATUSBAR | |
508 | } |