Ticket #2155: tutorial.qbk

File tutorial.qbk, 28.4 KB (added by Daniel James, 14 years ago)
Line 
1[/
2 / Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
3 /
4 / Distributed under the Boost Software License, Version 1.0. (See accompanying
5 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 /]
7
8[library Boost.Asio
9 [quickbook 1.4]
10 [copyright 2003 - 2008 Christopher M. Kohlhoff]
11 [license
12 Distributed under the Boost Software License, Version 1.0.
13 (See accompanying file LICENSE_1_0.txt or copy at
14 [@http://www.boost.org/LICENSE_1_0.txt])
15 ]
16 [category generic]
17]
18
19[section:tutorial Tutorial]
20
21[section:tuttimer4 Timer.4 - Using a member function as a handler]
22[section:src Source listing for Timer.4]
23
24 ``''''''``//
25 ``''''''``// timer.cpp
26 ``''''''``// ~~~~~~~~~
27 ``''''''``//
28 ``''''''``// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
29 ``''''''``//
30 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying
31 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
32 ``''''''``//
33
34 ``''''''``#include <iostream>
35 ``''''''``#include <boost/asio.hpp>
36 ``''''''``#include <boost/bind.hpp>
37 ``''''''``#include <boost/date_time/posix_time/posix_time.hpp>
38
39 ``''''''``class printer
40 ``''''''``{
41 ``''''''``public:
42 ``''''''`` printer(boost::asio::io_service& io)
43 ``''''''`` : timer_(io, boost::posix_time::seconds(1)),
44 ``''''''`` count_(0)
45 ``''''''`` {
46 ``''''''`` timer_.async_wait(boost::bind(&printer::print, this));
47 ``''''''`` }
48
49 ``''''''`` ~printer()
50 ``''''''`` {
51 ``''''''`` std::cout << "Final count is " << count_ << "\n";
52 ``''''''`` }
53
54 ``''''''`` void print()
55 ``''''''`` {
56 ``''''''`` if (count_ < 5)
57 ``''''''`` {
58 ``''''''`` std::cout << count_ << "\n";
59 ``''''''`` ++count_;
60
61 ``''''''`` timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
62 ``''''''`` timer_.async_wait(boost::bind(&printer::print, this));
63 ``''''''`` }
64 ``''''''`` }
65
66 ``''''''``private:
67 ``''''''`` boost::asio::deadline_timer timer_;
68 ``''''''`` int count_;
69 ``''''''``};
70
71 ``''''''``int main()
72 ``''''''``{
73 ``''''''`` boost::asio::io_service io;
74 ``''''''`` printer p(io);
75 ``''''''`` io.run();
76
77 ``''''''`` return 0;
78 ``''''''``}
79
80[endsect]
81[endsect]
82
83[section:tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs]
84
85If you find yourself running into these limitations, an alternative approach is to have a pool of threads calling boost::asio::io\_service::run(). However, as this allows handlers to execute concurrently, we need a method of synchronisation when handlers might be accessing a shared, thread-unsafe resource.
86
87 ``''''''``#include <iostream>
88 ``''''''``#include <boost/asio.hpp>
89 ``''''''``#include <boost/thread.hpp>
90 ``''''''``#include <boost/bind.hpp>
91 ``''''''``#include <boost/date_time/posix_time/posix_time.hpp>
92
93When initiating the asynchronous operations, each callback handler is "wrapped" using the boost::asio::strand object. The boost::asio::strand::wrap() function returns a new handler that automatically dispatches its contained handler through the boost::asio::strand object. By wrapping the handlers using the same boost::asio::strand, we are ensuring that they cannot execute concurrently.
94
95 ``''''''`` timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
96 ``''''''`` timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
97 ``''''''`` }
98
99 ``''''''`` ~printer()
100 ``''''''`` {
101 ``''''''`` std::cout << "Final count is " << count_ << "\n";
102 ``''''''`` }
103
104In a multithreaded program, the handlers for asynchronous operations should be synchronised if they access shared resources. In this tutorial, the shared resources used by the handlers (`print1` and `print2`) are `std::cout` and the `count_` data member.
105
106 ``''''''`` void print1()
107 ``''''''`` {
108 ``''''''`` if (count_ < 10)
109 ``''''''`` {
110 ``''''''`` std::cout << "Timer 1: " << count_ << "\n";
111 ``''''''`` ++count_;
112
113 ``''''''`` timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
114 ``''''''`` timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
115 ``''''''`` }
116 ``''''''`` }
117
118 ``''''''`` void print2()
119 ``''''''`` {
120 ``''''''`` if (count_ < 10)
121 ``''''''`` {
122 ``''''''`` std::cout << "Timer 2: " << count_ << "\n";
123 ``''''''`` ++count_;
124
125 ``''''''`` timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
126 ``''''''`` timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
127 ``''''''`` }
128 ``''''''`` }
129
130 ``''''''``private:
131 ``''''''`` boost::asio::strand strand_;
132 ``''''''`` boost::asio::deadline_timer timer1_;
133 ``''''''`` boost::asio::deadline_timer timer2_;
134 ``''''''`` int count_;
135 ``''''''``};
136
137The `main` function now causes boost::asio::io\_service::run() to be called from two threads: the main thread and one additional thread. This is accomplished using an boost::thread object.
138
139Just as it would with a call from a single thread, concurrent calls to boost::asio::io\_service::run() will continue to execute while there is "work" left to do. The background thread will not exit until all asynchronous operations have completed.
140
141
142
143 ``''''''``int main()
144 ``''''''``{
145 ``''''''`` boost::asio::io_service io;
146 ``''''''`` printer p(io);
147 ``''''''`` boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
148 ``''''''`` io.run();
149 ``''''''`` t.join();
150
151 ``''''''`` return 0;
152 ``''''''``}
153
154[section:src Source listing for Timer.5]
155[endsect]
156
157[endsect]
158
159[section:tutdaytime1 Daytime.1 - A synchronous TCP daytime client]
160
161This tutorial program shows how to use asio to implement a client application with TCP.
162
163
164
165
166
167We start by including the necessary header files.
168
169
170 ``''''''``#include <iostream>
171 ``''''''``#include <boost/array.hpp>
172 ``''''''``#include <boost/asio.hpp>
173
174
175
176The purpose of this application is to access a daytime service, so we need the user to specify the server.
177
178
179
180 ``''''''``using boost::asio::ip::tcp;
181
182 ``''''''``int main(int argc, char* argv[])
183 ``''''''``{
184 ``''''''`` try
185 ``''''''`` {
186 ``''''''`` if (argc != 2)
187 ``''''''`` {
188 ``''''''`` std::cerr << "Usage: client <host>" << std::endl;
189 ``''''''`` return 1;
190 ``''''''`` }
191
192
193
194All programs that use asio need to have at least one boost::asio::io\_service object.
195
196
197
198 ``''''''`` boost::asio::io_service io_service;
199
200
201
202We need to turn the server name that was specified as a parameter to the application, into a TCP endpoint. To do this we use an boost::asio::ip::tcp::resolver object.
203
204
205
206 ``''''''`` tcp::resolver resolver(io_service);
207
208
209
210A resolver takes a query object and turns it into a list of endpoints. We construct a query using the name of the server, specified in `argv[1]`, and the name of the service, in this case `"daytime"`.
211
212
213 ``''''''`` tcp::resolver::query query(argv[1], "daytime");
214
215
216
217The list of endpoints is returned using an iterator of type boost::asio::ip::tcp::resolver::iterator. A default constructed boost::asio::ip::tcp::resolver::iterator object is used as the end iterator.
218
219
220 ``''''''`` tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
221 ``''''''`` tcp::resolver::iterator end;
222
223
224
225Now we create and connect the socket. The list of endpoints obtained above may contain both IPv4 and IPv6 endpoints, so we need to try each of them until we find one that works. This keeps the client program independent of a specific IP version.
226
227
228
229 ``''''''`` tcp::socket socket(io_service);
230 ``''''''`` boost::system::error_code error = boost::asio::error::host_not_found;
231 ``''''''`` while (error && endpoint_iterator != end)
232 ``''''''`` {
233 ``''''''`` socket.close();
234 ``''''''`` socket.connect(*endpoint_iterator++, error);
235 ``''''''`` }
236 ``''''''`` if (error)
237 ``''''''`` throw boost::system::system_error(error);
238
239
240
241The connection is open. All we need to do now is read the response from the daytime service.
242
243We use a `boost::array` to hold the received data. The boost::asio::buffer() function automatically determines the size of the array to help prevent buffer overruns. Instead of a `boost::array`, we could have used a `char []` or `std::vector`.
244
245
246
247 ``''''''`` for (;;)
248 ``''''''`` {
249 ``''''''`` boost::array<char, 128> buf;
250 ``''''''`` boost::system::error_code error;
251
252 ``''''''`` size_t len = socket.read_some(boost::asio::buffer(buf), error);
253
254
255
256When the server closes the connection, the boost::asio::ip::tcp::socket::read\_some() function will exit with the boost::asio::error::eof error, which is how we know to exit the loop.
257
258
259
260 ``''''''`` if (error == boost::asio::error::eof)
261 ``''''''`` break; // Connection closed cleanly by peer.
262 ``''''''`` else if (error)
263 ``''''''`` throw boost::system::system_error(error); // Some other error.
264
265 ``''''''`` std::cout.write(buf.data(), len);
266 ``''''''`` }
267
268
269
270Finally, handle any exceptions that may have been thrown.
271
272
273 ``''''''`` }
274 ``''''''`` catch (std::exception& e)
275 ``''''''`` {
276 ``''''''`` std::cerr << e.what() << std::endl;
277 ``''''''`` }
278
279
280
281See the [link boost_asio.tutorial.tutdaytime1.src full source listing]
282
283Return to the [link boost_asio.tutorial tutorial index]
284
285Next: [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server]
286
287
288
289[section:src Source listing for Daytime.1]
290
291
292 ``''''''``//
293 ``''''''``// client.cpp
294 ``''''''``// ~~~~~~~~~~
295 ``''''''``//
296 ``''''''``// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
297 ``''''''``//
298 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying
299 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
300 ``''''''``//
301
302 ``''''''``#include <iostream>
303 ``''''''``#include <boost/array.hpp>
304 ``''''''``#include <boost/asio.hpp>
305
306 ``''''''``using boost::asio::ip::tcp;
307
308 ``''''''``int main(int argc, char* argv[])
309 ``''''''``{
310 ``''''''`` try
311 ``''''''`` {
312 ``''''''`` if (argc != 2)
313 ``''''''`` {
314 ``''''''`` std::cerr << "Usage: client <host>" << std::endl;
315 ``''''''`` return 1;
316 ``''''''`` }
317
318 ``''''''`` boost::asio::io_service io_service;
319
320 ``''''''`` tcp::resolver resolver(io_service);
321 ``''''''`` tcp::resolver::query query(argv[1], "daytime");
322 ``''''''`` tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
323 ``''''''`` tcp::resolver::iterator end;
324
325 ``''''''`` tcp::socket socket(io_service);
326 ``''''''`` boost::system::error_code error = boost::asio::error::host_not_found;
327 ``''''''`` while (error && endpoint_iterator != end)
328 ``''''''`` {
329 ``''''''`` socket.close();
330 ``''''''`` socket.connect(*endpoint_iterator++, error);
331 ``''''''`` }
332 ``''''''`` if (error)
333 ``''''''`` throw boost::system::system_error(error);
334
335 ``''''''`` for (;;)
336 ``''''''`` {
337 ``''''''`` boost::array<char, 128> buf;
338 ``''''''`` boost::system::error_code error;
339
340 ``''''''`` size_t len = socket.read_some(boost::asio::buffer(buf), error);
341
342 ``''''''`` if (error == boost::asio::error::eof)
343 ``''''''`` break; // Connection closed cleanly by peer.
344 ``''''''`` else if (error)
345 ``''''''`` throw boost::system::system_error(error); // Some other error.
346
347 ``''''''`` std::cout.write(buf.data(), len);
348 ``''''''`` }
349 ``''''''`` }
350 ``''''''`` catch (std::exception& e)
351 ``''''''`` {
352 ``''''''`` std::cerr << e.what() << std::endl;
353 ``''''''`` }
354
355 ``''''''`` return 0;
356 ``''''''``}
357
358Return to [link boost_asio.tutorial.tutdaytime1 Daytime.1 - A synchronous TCP daytime client]
359
360[endsect]
361
362[endsect]
363
364[section:tutdaytime2 Daytime.2 - A synchronous TCP daytime server]
365
366This tutorial program shows how to use asio to implement a server application with TCP.
367
368
369
370
371
372
373 ``''''''``#include <ctime>
374 ``''''''``#include <iostream>
375 ``''''''``#include <string>
376 ``''''''``#include <boost/asio.hpp>
377
378 ``''''''``using boost::asio::ip::tcp;
379
380
381
382We define the function `make_daytime_string()` to create the string to be sent back to the client. This function will be reused in all of our daytime server applications.
383
384
385
386 ``''''''``std::string make_daytime_string()
387 ``''''''``{
388 ``''''''`` using namespace std; // For time_t, time and ctime;
389 ``''''''`` time_t now = time(0);
390 ``''''''`` return ctime(&now);
391 ``''''''``}
392
393 ``''''''``int main()
394 ``''''''``{
395 ``''''''`` try
396 ``''''''`` {
397 ``''''''`` boost::asio::io_service io_service;
398
399
400
401A boost::asio::ip::tcp::acceptor object needs to be created to listen for new connections. It is initialised to listen on TCP port 13, for IP version 4.
402
403
404
405 ``''''''`` tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
406
407
408
409This is an iterative server, which means that it will handle one connection at a time. Create a socket that will represent the connection to the client, and then wait for a connection.
410
411
412
413 ``''''''`` for (;;)
414 ``''''''`` {
415 ``''''''`` tcp::socket socket(io_service);
416 ``''''''`` acceptor.accept(socket);
417
418
419
420A client is accessing our service. Determine the current time and transfer this information to the client.
421
422
423
424 ``''''''`` std::string message = make_daytime_string();
425
426 ``''''''`` boost::system::error_code ignored_error;
427 ``''''''`` boost::asio::write(socket, boost::asio::buffer(message),
428 ``''''''`` boost::asio::transfer_all(), ignored_error);
429 ``''''''`` }
430 ``''''''`` }
431
432
433
434Finally, handle any exceptions.
435
436
437 ``''''''`` catch (std::exception& e)
438 ``''''''`` {
439 ``''''''`` std::cerr << e.what() << std::endl;
440 ``''''''`` }
441
442 ``''''''`` return 0;
443 ``''''''``}
444
445
446
447See the [link boost_asio.tutorial.tutdaytime2.src full source listing]
448
449Return to the [link boost_asio.tutorial tutorial index]
450
451Previous: [link boost_asio.tutorial.tutdaytime1 Daytime.1 - A synchronous TCP daytime client]
452
453Next: [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server]
454
455
456
457[section:src Source listing for Daytime.2]
458
459
460 ``''''''``//
461 ``''''''``// server.cpp
462 ``''''''``// ~~~~~~~~~~
463 ``''''''``//
464 ``''''''``// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
465 ``''''''``//
466 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying
467 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
468 ``''''''``//
469
470 ``''''''``#include <ctime>
471 ``''''''``#include <iostream>
472 ``''''''``#include <string>
473 ``''''''``#include <boost/asio.hpp>
474
475 ``''''''``using boost::asio::ip::tcp;
476
477 ``''''''``std::string make_daytime_string()
478 ``''''''``{
479 ``''''''`` using namespace std; // For time_t, time and ctime;
480 ``''''''`` time_t now = time(0);
481 ``''''''`` return ctime(&now);
482 ``''''''``}
483
484 ``''''''``int main()
485 ``''''''``{
486 ``''''''`` try
487 ``''''''`` {
488 ``''''''`` boost::asio::io_service io_service;
489
490 ``''''''`` tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
491
492 ``''''''`` for (;;)
493 ``''''''`` {
494 ``''''''`` tcp::socket socket(io_service);
495 ``''''''`` acceptor.accept(socket);
496
497 ``''''''`` std::string message = make_daytime_string();
498
499 ``''''''`` boost::system::error_code ignored_error;
500 ``''''''`` boost::asio::write(socket, boost::asio::buffer(message),
501 ``''''''`` boost::asio::transfer_all(), ignored_error);
502 ``''''''`` }
503 ``''''''`` }
504 ``''''''`` catch (std::exception& e)
505 ``''''''`` {
506 ``''''''`` std::cerr << e.what() << std::endl;
507 ``''''''`` }
508
509 ``''''''`` return 0;
510 ``''''''``}
511
512Return to [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server]
513
514[endsect]
515
516[endsect]
517
518[section:tutdaytime3 Daytime.3 - An asynchronous TCP daytime server]
519
520[heading The main() function]
521
522
523 ``''''''``int main()
524 ``''''''``{
525 ``''''''`` try
526 ``''''''`` {
527
528
529
530We need to create a server object to accept incoming client connections. The boost::asio::io\_service object provides I/O services, such as sockets, that the server object will use.
531
532
533 ``''''''`` boost::asio::io_service io_service;
534 ``''''''`` tcp_server server(io_service);
535
536
537
538Run the boost::asio::io\_service object so that it will perform asynchronous operations on your behalf.
539
540
541 ``''''''`` io_service.run();
542 ``''''''`` }
543 ``''''''`` catch (std::exception& e)
544 ``''''''`` {
545 ``''''''`` std::cerr << e.what() << std::endl;
546 ``''''''`` }
547
548 ``''''''`` return 0;
549 ``''''''``}
550
551
552
553[heading The tcp_server class]
554
555
556 ``''''''``class tcp_server
557 ``''''''``{
558 ``''''''``public:
559
560
561
562The constructor initialises an acceptor to listen on TCP port 13.
563
564
565 ``''''''`` tcp_server(boost::asio::io_service& io_service)
566 ``''''''`` : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))
567 ``''''''`` {
568 ``''''''`` start_accept();
569 ``''''''`` }
570
571 ``''''''``private:
572
573
574
575The function `start_accept()` creates a socket and initiates an asynchronous accept operation to wait for a new connection.
576
577
578 ``''''''`` void start_accept()
579 ``''''''`` {
580 ``''''''`` tcp_connection::pointer new_connection =
581 ``''''''`` tcp_connection::create(acceptor_.io_service());
582
583 ``''''''`` acceptor_.async_accept(new_connection->socket(),
584 ``''''''`` boost::bind(&tcp_server::handle_accept, this, new_connection,
585 ``''''''`` boost::asio::placeholders::error));
586 ``''''''`` }
587
588
589
590The function `handle_accept()` is called when the asynchronous accept operation initiated by `start_accept()` finishes. It services the client request, and then calls `start_accept()` to initiate the next accept operation.
591
592
593
594 ``''''''`` void handle_accept(tcp_connection::pointer new_connection,
595 ``''''''`` const boost::system::error_code& error)
596 ``''''''`` {
597 ``''''''`` if (!error)
598 ``''''''`` {
599 ``''''''`` new_connection->start();
600 ``''''''`` start_accept();
601 ``''''''`` }
602 ``''''''`` }
603
604
605
606[heading The tcp_connection class]
607
608We will use `shared_ptr` and `enable_shared_from_this` because we want to keep the `tcp_connection` object alive as long as there is an operation that refers to it.
609
610
611 ``''''''``class tcp_connection
612 ``''''''`` : public boost::enable_shared_from_this<tcp_connection>
613 ``''''''``{
614 ``''''''``public:
615 ``''''''`` typedef boost::shared_ptr<tcp_connection> pointer;
616
617 ``''''''`` static pointer create(boost::asio::io_service& io_service)
618 ``''''''`` {
619 ``''''''`` return pointer(new tcp_connection(io_service));
620 ``''''''`` }
621
622 ``''''''`` tcp::socket& socket()
623 ``''''''`` {
624 ``''''''`` return socket_;
625 ``''''''`` }
626
627
628
629In the function `start()`, we call boost::asio::async\_write() to serve the data to the client. Note that we are using boost::asio::async\_write(), rather than boost::asio::ip::tcp::socket::async\_write\_some(), to ensure that the entire block of data is sent.
630
631
632
633 ``''''''`` void start()
634 ``''''''`` {
635
636
637
638The data to be sent is stored in the class member `message_` as we need to keep the data valid until the asynchronous operation is complete.
639
640
641 ``''''''`` message_ = make_daytime_string();
642
643
644
645When initiating the asynchronous operation, and if using boost::bind(), you must specify only the arguments that match the handler's parameter list. In this program, both of the argument placeholders (boost::asio::placeholders::error and boost::asio::placeholders::bytes\_transferred) could potentially have been removed, since they are not being used in `handle_write()`.
646
647
648
649 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_),
650 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this(),
651 ``''''''`` boost::asio::placeholders::error,
652 ``''''''`` boost::asio::placeholders::bytes_transferred));
653
654
655
656Any further actions for this client connection are now the responsibility of `handle_write()`.
657
658
659 ``''''''`` }
660
661 ``''''''``private:
662 ``''''''`` tcp_connection(boost::asio::io_service& io_service)
663 ``''''''`` : socket_(io_service)
664 ``''''''`` {
665 ``''''''`` }
666
667 ``''''''`` void handle_write(const boost::system::error_code& /*error*/,
668 ``''''''`` size_t /*bytes_transferred*/)
669 ``''''''`` {
670 ``''''''`` }
671
672 ``''''''`` tcp::socket socket_;
673 ``''''''`` std::string message_;
674 ``''''''``};
675
676
677
678[heading Removing unused handler parameters]
679
680You may have noticed that the `error`, and `bytes_transferred` parameters are not used in the body of the `handle_write()` function. If parameters are not needed, it is possible to remove them from the function so that it looks like:
681
682
683 ``''''''`` void handle_write()
684 ``''''''`` {
685 ``''''''`` }
686
687
688
689The boost::asio::async\_write() call used to initiate the call can then be changed to just:
690
691
692 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_),
693 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this()));
694
695
696
697See the [link boost_asio.tutorial.tutdaytime3.src full source listing]
698
699Return to the [link boost_asio.tutorial tutorial index]
700
701Previous: [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server]
702
703Next: [link boost_asio.tutorial.tutdaytime4 Daytime.4 - A synchronous UDP daytime client]
704
705
706
707[section:src Source listing for Daytime.3]
708
709
710 ``''''''``//
711 ``''''''``// server.cpp
712 ``''''''``// ~~~~~~~~~~
713 ``''''''``//
714 ``''''''``// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
715 ``''''''``//
716 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying
717 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
718 ``''''''``//
719
720 ``''''''``#include <ctime>
721 ``''''''``#include <iostream>
722 ``''''''``#include <string>
723 ``''''''``#include <boost/bind.hpp>
724 ``''''''``#include <boost/shared_ptr.hpp>
725 ``''''''``#include <boost/enable_shared_from_this.hpp>
726 ``''''''``#include <boost/asio.hpp>
727
728 ``''''''``using boost::asio::ip::tcp;
729
730 ``''''''``std::string make_daytime_string()
731 ``''''''``{
732 ``''''''`` using namespace std; // For time_t, time and ctime;
733 ``''''''`` time_t now = time(0);
734 ``''''''`` return ctime(&now);
735 ``''''''``}
736
737 ``''''''``class tcp_connection
738 ``''''''`` : public boost::enable_shared_from_this<tcp_connection>
739 ``''''''``{
740 ``''''''``public:
741 ``''''''`` typedef boost::shared_ptr<tcp_connection> pointer;
742
743 ``''''''`` static pointer create(boost::asio::io_service& io_service)
744 ``''''''`` {
745 ``''''''`` return pointer(new tcp_connection(io_service));
746 ``''''''`` }
747
748 ``''''''`` tcp::socket& socket()
749 ``''''''`` {
750 ``''''''`` return socket_;
751 ``''''''`` }
752
753 ``''''''`` void start()
754 ``''''''`` {
755 ``''''''`` message_ = make_daytime_string();
756
757 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_),
758 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this(),
759 ``''''''`` boost::asio::placeholders::error,
760 ``''''''`` boost::asio::placeholders::bytes_transferred));
761 ``''''''`` }
762
763 ``''''''``private:
764 ``''''''`` tcp_connection(boost::asio::io_service& io_service)
765 ``''''''`` : socket_(io_service)
766 ``''''''`` {
767 ``''''''`` }
768
769 ``''''''`` void handle_write(const boost::system::error_code& /*error*/,
770 ``''''''`` size_t /*bytes_transferred*/)
771 ``''''''`` {
772 ``''''''`` }
773
774 ``''''''`` tcp::socket socket_;
775 ``''''''`` std::string message_;
776 ``''''''``};
777
778 ``''''''``class tcp_server
779 ``''''''``{
780 ``''''''``public:
781 ``''''''`` tcp_server(boost::asio::io_service& io_service)
782 ``''''''`` : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))
783 ``''''''`` {
784 ``''''''`` start_accept();
785 ``''''''`` }
786
787 ``''''''``private:
788 ``''''''`` void start_accept()
789 ``''''''`` {
790 ``''''''`` tcp_connection::pointer new_connection =
791 ``''''''`` tcp_connection::create(acceptor_.io_service());
792
793 ``''''''`` acceptor_.async_accept(new_connection->socket(),
794 ``''''''`` boost::bind(&tcp_server::handle_accept, this, new_connection,
795 ``''''''`` boost::asio::placeholders::error));
796 ``''''''`` }
797
798 ``''''''`` void handle_accept(tcp_connection::pointer new_connection,
799 ``''''''`` const boost::system::error_code& error)
800 ``''''''`` {
801 ``''''''`` if (!error)
802 ``''''''`` {
803 ``''''''`` new_connection->start();
804 ``''''''`` start_accept();
805 ``''''''`` }
806 ``''''''`` }
807
808 ``''''''`` tcp::acceptor acceptor_;
809 ``''''''``};
810
811 ``''''''``int main()
812 ``''''''``{
813 ``''''''`` try
814 ``''''''`` {
815 ``''''''`` boost::asio::io_service io_service;
816 ``''''''`` tcp_server server(io_service);
817 ``''''''`` io_service.run();
818 ``''''''`` }
819 ``''''''`` catch (std::exception& e)
820 ``''''''`` {
821 ``''''''`` std::cerr << e.what() << std::endl;
822 ``''''''`` }
823
824 ``''''''`` return 0;
825 ``''''''``}
826
827Return to [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server]
828
829[endsect]
830
831[endsect]
832
833[section:tutdaytime4 Daytime.4 - A synchronous UDP daytime client]
834
835This tutorial program shows how to use asio to implement a client application with UDP.
836
837
838 ``''''''``#include <iostream>
839 ``''''''``#include <boost/array.hpp>
840 ``''''''``#include <boost/asio.hpp>
841
842 ``''''''``using boost::asio::ip::udp;
843
844
845
846The start of the application is essentially the same as for the TCP daytime client.
847
848
849
850 ``''''''``int main(int argc, char* argv[])
851 ``''''''``{
852 ``''''''`` try
853 ``''''''`` {
854 ``''''''`` if (argc != 2)
855 ``''''''`` {
856 ``''''''`` std::cerr << "Usage: client <host>" << std::endl;
857 ``''''''`` return 1;
858 ``''''''`` }
859
860 ``''''''`` boost::asio::io_service io_service;
861
862
863
864We use an boost::asio::ip::udp::resolver object to find the correct remote endpoint to use based on the host and service names. The query is restricted to return only IPv4 endpoints by the boost::asio::ip::udp::v4() argument.
865
866
867
868 ``''''''`` udp::resolver resolver(io_service);
869 ``''''''`` udp::resolver::query query(udp::v4(), argv[1], "daytime");
870
871
872
873The boost::asio::ip::udp::resolver::resolve() function is guaranteed to return at least one endpoint in the list if it does not fail. This means it is safe to dereference the return value directly.
874
875
876 ``''''''`` udp::endpoint receiver_endpoint = *resolver.resolve(query);
877
878
879
880Since UDP is datagram-oriented, we will not be using a stream socket. Create an boost::asio::ip::udp::socket and initiate contact with the remote endpoint.
881
882
883
884 ``''''''`` udp::socket socket(io_service);
885 ``''''''`` socket.open(udp::v4());
886
887 ``''''''`` boost::array<char, 1> send_buf = { 0 };
888 ``''''''`` socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
889
890
891
892Now we need to be ready to accept whatever the server sends back to us. The endpoint on our side that receives the server's response will be initialised by boost::asio::ip::udp::socket::receive\_from().
893
894
895
896 ``''''''`` boost::array<char, 128> recv_buf;
897 ``''''''`` udp::endpoint sender_endpoint;
898 ``''''''`` size_t len = socket.receive_from(
899 ``''''''`` boost::asio::buffer(recv_buf), sender_endpoint);
900
901 ``''''''`` std::cout.write(recv_buf.data(), len);
902 ``''''''`` }
903
904
905
906Finally, handle any exceptions that may have been thrown.
907
908
909 ``''''''`` catch (std::exception& e)
910 ``''''''`` {
911 ``''''''`` std::cerr << e.what() << std::endl;
912 ``''''''`` }
913
914 ``''''''`` return 0;
915 ``''''''``}
916
917See the [link boost_asio.tutorial.tutdaytime4.src full source listing]
918
919Return to the [link boost_asio.tutorial tutorial index]
920
921Previous: [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server]
922
923Next: [link boost_asio.tutorial.tutdaytime5 Daytime.5 - A synchronous UDP daytime server]
924
925[endsect]
926[endsect]