1[/ 2 / Copyright (c) 2003-2021 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[section:tutorial Tutorial] 9 10[heading Basic Skills] 11 12The tutorial programs in this first section introduce the fundamental concepts required to use the asio toolkit. Before plunging into the complex world of network programming, these tutorial programs illustrate the basic skills using simple asynchronous timers. 13 14* [link boost_asio.tutorial.tuttimer1 Timer.1 - Using a timer synchronously] 15 16 17* [link boost_asio.tutorial.tuttimer2 Timer.2 - Using a timer asynchronously] 18 19 20* [link boost_asio.tutorial.tuttimer3 Timer.3 - Binding arguments to a handler] 21 22 23* [link boost_asio.tutorial.tuttimer4 Timer.4 - Using a member function as a handler] 24 25 26* [link boost_asio.tutorial.tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs] 27 28 29 30 31[heading Introduction to Sockets] 32 33The tutorial programs in this section show how to use asio to develop simple client and server programs. These tutorial programs are based around the [@http://www.ietf.org/rfc/rfc867.txt daytime] protocol, which supports both TCP and UDP. 34 35The first three tutorial programs implement the daytime protocol using TCP. 36 37* [link boost_asio.tutorial.tutdaytime1 Daytime.1 - A synchronous TCP daytime client] 38 39 40* [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server] 41 42 43* [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server] 44 45 46 47The next three tutorial programs implement the daytime protocol using UDP. 48 49* [link boost_asio.tutorial.tutdaytime4 Daytime.4 - A synchronous UDP daytime client] 50 51 52* [link boost_asio.tutorial.tutdaytime5 Daytime.5 - A synchronous UDP daytime server] 53 54 55* [link boost_asio.tutorial.tutdaytime6 Daytime.6 - An asynchronous UDP daytime server] 56 57 58 59The last tutorial program in this section demonstrates how asio allows the TCP and UDP servers to be easily combined into a single program. 60 61* [link boost_asio.tutorial.tutdaytime7 Daytime.7 - A combined TCP/UDP asynchronous server] 62 63 64 65 66[section:tuttimer1 Timer.1 - Using a timer synchronously] 67 68This tutorial program introduces asio by showing how to perform a blocking wait on a timer. 69 70 71 72 73 74We start by including the necessary header files. 75 76All of the asio classes can be used by simply including the `"asio.hpp"` header file. 77 78 79 ``''''''``#include <iostream> 80 ``''''''``#include <boost/asio.hpp> 81 82 83 84All programs that use asio need to have at least one I/O execution context, such as an 85[link boost_asio.reference.io_context io_context] or 86[link boost_asio.reference.thread_pool thread_pool] object. An I/O execution context provides access to I/O functionality. We declare an object of type 87[link boost_asio.reference.io_context io_context] first thing in the main function. 88 89 90 91 ``''''''``int main() 92 ``''''''``{ 93 ``''''''`` boost::asio::io_context io; 94 95 96 97Next we declare an object of type boost::asio::steady\_timer. The core asio classes that provide I/O functionality (or as in this case timer functionality) always take a reference to an io\_context as their first constructor argument. The second argument to the constructor sets the timer to expire 5 seconds from now. 98 99 100 101 ``''''''`` boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); 102 103 104 105In this simple example we perform a blocking wait on the timer. That is, the call to [link boost_asio.reference.basic_waitable_timer.wait steady_timer::wait()] will not return until the timer has expired, 5 seconds after it was created (i.e. not from when the wait starts). 106 107A timer is always in one of two states: "expired" or "not expired". If the [link boost_asio.reference.basic_waitable_timer.wait steady_timer::wait()] function is called on an expired timer, it will return immediately. 108 109 110 ``''''''`` t.wait(); 111 112 113 114Finally we print the obligatory `"Hello, world!"` message to show when the timer has expired. 115 116 117 118 ``''''''`` std::cout << "Hello, world!" << std::endl; 119 120 ``''''''`` return 0; 121 ``''''''``} 122 123 124 125See the [link boost_asio.tutorial.tuttimer1.src full source listing] 126 127Return to the [link boost_asio.tutorial tutorial index] 128 129Next: [link boost_asio.tutorial.tuttimer2 Timer.2 - Using a timer asynchronously] 130 131 132 133[section:src Source listing for Timer.1] 134 135 136 ``''''''``// 137 ``''''''``// timer.cpp 138 ``''''''``// ~~~~~~~~~ 139 ``''''''``// 140 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 141 ``''''''``// 142 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 143 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 144 ``''''''``// 145 146 ``''''''``#include <iostream> 147 ``''''''``#include <boost/asio.hpp> 148 149 ``''''''``int main() 150 ``''''''``{ 151 ``''''''`` boost::asio::io_context io; 152 153 ``''''''`` boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); 154 ``''''''`` t.wait(); 155 156 ``''''''`` std::cout << "Hello, world!" << std::endl; 157 158 ``''''''`` return 0; 159 ``''''''``} 160 161Return to [link boost_asio.tutorial.tuttimer1 Timer.1 - Using a timer synchronously] 162 163[endsect] 164 165[endsect] 166 167[section:tuttimer2 Timer.2 - Using a timer asynchronously] 168 169This tutorial program demonstrates how to use asio's asynchronous callback functionality by modifying the program from tutorial Timer.1 to perform an asynchronous wait on the timer. 170 171 172 173 174 175 176 ``''''''``#include <iostream> 177 ``''''''``#include <boost/asio.hpp> 178 179 180 181Using asio's asynchronous functionality means having a callback function that will be called when an asynchronous operation completes. In this program we define a function called `print` to be called when the asynchronous wait finishes. 182 183 184 185 ``''''''``void print(const boost::system::error_code& /*e*/) 186 ``''''''``{ 187 ``''''''`` std::cout << "Hello, world!" << std::endl; 188 ``''''''``} 189 190 ``''''''``int main() 191 ``''''''``{ 192 ``''''''`` boost::asio::io_context io; 193 194 ``''''''`` boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); 195 196 197 198Next, instead of doing a blocking wait as in tutorial Timer.1, we call the [link boost_asio.reference.basic_waitable_timer.async_wait steady_timer::async_wait()] function to perform an asynchronous wait. When calling this function we pass the `print` callback handler that was defined above. 199 200 201 ``''''''`` t.async_wait(&print); 202 203 204 205Finally, we must call the [link boost_asio.reference.io_context.run io_context::run()] member function on the io\_context object. 206 207The asio library provides a guarantee that callback handlers will only be called from threads that are currently calling [link boost_asio.reference.io_context.run io_context::run()]. Therefore unless the [link boost_asio.reference.io_context.run io_context::run()] function is called the callback for the asynchronous wait completion will never be invoked. 208 209The [link boost_asio.reference.io_context.run io_context::run()] function will also continue to run while there is still "work" to do. In this example, the work is the asynchronous wait on the timer, so the call will not return until the timer has expired and the callback has completed. 210 211It is important to remember to give the io\_context some work to do before calling [link boost_asio.reference.io_context.run io_context::run()]. For example, if we had omitted the above call to [link boost_asio.reference.basic_waitable_timer.async_wait steady_timer::async_wait()], the io\_context would not have had any work to do, and consequently [link boost_asio.reference.io_context.run io_context::run()] would have returned immediately. 212 213 214 ``''''''`` io.run(); 215 216 ``''''''`` return 0; 217 ``''''''``} 218 219 220 221See the [link boost_asio.tutorial.tuttimer2.src full source listing] 222 223Return to the [link boost_asio.tutorial tutorial index] 224 225Previous: [link boost_asio.tutorial.tuttimer1 Timer.1 - Using a timer synchronously] 226 227Next: [link boost_asio.tutorial.tuttimer3 Timer.3 - Binding arguments to a handler] 228 229 230 231[section:src Source listing for Timer.2] 232 233 234 ``''''''``// 235 ``''''''``// timer.cpp 236 ``''''''``// ~~~~~~~~~ 237 ``''''''``// 238 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 239 ``''''''``// 240 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 241 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 242 ``''''''``// 243 244 ``''''''``#include <iostream> 245 ``''''''``#include <boost/asio.hpp> 246 247 ``''''''``void print(const boost::system::error_code& /*e*/) 248 ``''''''``{ 249 ``''''''`` std::cout << "Hello, world!" << std::endl; 250 ``''''''``} 251 252 ``''''''``int main() 253 ``''''''``{ 254 ``''''''`` boost::asio::io_context io; 255 256 ``''''''`` boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); 257 ``''''''`` t.async_wait(&print); 258 259 ``''''''`` io.run(); 260 261 ``''''''`` return 0; 262 ``''''''``} 263 264Return to [link boost_asio.tutorial.tuttimer2 Timer.2 - Using a timer asynchronously] 265 266[endsect] 267 268[endsect] 269 270[section:tuttimer3 Timer.3 - Binding arguments to a handler] 271 272In this tutorial we will modify the program from tutorial Timer.2 so that the timer fires once a second. This will show how to pass additional parameters to your handler function. 273 274 275 276 277 278 279 ``''''''``#include <iostream> 280 ``''''''``#include <boost/asio.hpp> 281 ``''''''``#include <boost/bind/bind.hpp> 282 283 284 285To implement a repeating timer using asio you need to change the timer's expiry time in your callback function, and to then start a new asynchronous wait. Obviously this means that the callback function will need to be able to access the timer object. To this end we add two new parameters to the `print` function: 286 287* A pointer to a timer object. 288 289 290* A counter so that we can stop the program when the timer fires for the sixth time. 291 292 293 294 295 ``''''''``void print(const boost::system::error_code& /*e*/, 296 ``''''''`` boost::asio::steady_timer* t, int* count) 297 ``''''''``{ 298 299 300 301As mentioned above, this tutorial program uses a counter to stop running when the timer fires for the sixth time. However you will observe that there is no explicit call to ask the io\_context to stop. Recall that in tutorial Timer.2 we learnt that the [link boost_asio.reference.io_context.run io_context::run()] function completes when there is no more "work" to do. By not starting a new asynchronous wait on the timer when `count` reaches 5, the io\_context will run out of work and stop running. 302 303 304 ``''''''`` if (*count < 5) 305 ``''''''`` { 306 ``''''''`` std::cout << *count << std::endl; 307 ``''''''`` ++(*count); 308 309 310 311Next we move the expiry time for the timer along by one second from the previous expiry time. By calculating the new expiry time relative to the old, we can ensure that the timer does not drift away from the whole-second mark due to any delays in processing the handler. 312 313 314 315 ``''''''`` t->expires_at(t->expiry() + boost::asio::chrono::seconds(1)); 316 317 318 319Then we start a new asynchronous wait on the timer. As you can see, the boost::bind() function is used to associate the extra parameters with your callback handler. The [link boost_asio.reference.basic_waitable_timer.async_wait steady_timer::async_wait()] function expects a handler function (or function object) with the signature `void(const boost::system::error_code&)`. Binding the additional parameters converts your `print` function into a function object that matches the signature correctly. 320 321See the [@http://www.boost.org/libs/bind/bind.html Boost.Bind documentation] for more information on how to use boost::bind(). 322 323In this example, the boost::asio::placeholders::error argument to boost::bind() is a named placeholder for the error object passed to the handler. When initiating the asynchronous operation, and if using boost::bind(), you must specify only the arguments that match the handler's parameter list. In tutorial Timer.4 you will see that this placeholder may be elided if the parameter is not needed by the callback handler. 324 325 326 ``''''''`` t->async_wait(boost::bind(print, 327 ``''''''`` boost::asio::placeholders::error, t, count)); 328 ``''''''`` } 329 ``''''''``} 330 331 ``''''''``int main() 332 ``''''''``{ 333 ``''''''`` boost::asio::io_context io; 334 335 336 337A new `count` variable is added so that we can stop the program when the timer fires for the sixth time. 338 339 340 341 ``''''''`` int count = 0; 342 ``''''''`` boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1)); 343 344 345 346As in Step 4, when making the call to [link boost_asio.reference.basic_waitable_timer.async_wait steady_timer::async_wait()] from `main` we bind the additional parameters needed for the `print` function. 347 348 349 ``''''''`` t.async_wait(boost::bind(print, 350 ``''''''`` boost::asio::placeholders::error, &t, &count)); 351 352 ``''''''`` io.run(); 353 354 355 356Finally, just to prove that the `count` variable was being used in the `print` handler function, we will print out its new value. 357 358 359 360 ``''''''`` std::cout << "Final count is " << count << std::endl; 361 362 ``''''''`` return 0; 363 ``''''''``} 364 365 366 367See the [link boost_asio.tutorial.tuttimer3.src full source listing] 368 369Return to the [link boost_asio.tutorial tutorial index] 370 371Previous: [link boost_asio.tutorial.tuttimer2 Timer.2 - Using a timer asynchronously] 372 373Next: [link boost_asio.tutorial.tuttimer4 Timer.4 - Using a member function as a handler] 374 375 376 377[section:src Source listing for Timer.3] 378 379 380 ``''''''``// 381 ``''''''``// timer.cpp 382 ``''''''``// ~~~~~~~~~ 383 ``''''''``// 384 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 385 ``''''''``// 386 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 387 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 388 ``''''''``// 389 390 ``''''''``#include <iostream> 391 ``''''''``#include <boost/asio.hpp> 392 ``''''''``#include <boost/bind/bind.hpp> 393 394 ``''''''``void print(const boost::system::error_code& /*e*/, 395 ``''''''`` boost::asio::steady_timer* t, int* count) 396 ``''''''``{ 397 ``''''''`` if (*count < 5) 398 ``''''''`` { 399 ``''''''`` std::cout << *count << std::endl; 400 ``''''''`` ++(*count); 401 402 ``''''''`` t->expires_at(t->expiry() + boost::asio::chrono::seconds(1)); 403 ``''''''`` t->async_wait(boost::bind(print, 404 ``''''''`` boost::asio::placeholders::error, t, count)); 405 ``''''''`` } 406 ``''''''``} 407 408 ``''''''``int main() 409 ``''''''``{ 410 ``''''''`` boost::asio::io_context io; 411 412 ``''''''`` int count = 0; 413 ``''''''`` boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1)); 414 ``''''''`` t.async_wait(boost::bind(print, 415 ``''''''`` boost::asio::placeholders::error, &t, &count)); 416 417 ``''''''`` io.run(); 418 419 ``''''''`` std::cout << "Final count is " << count << std::endl; 420 421 ``''''''`` return 0; 422 ``''''''``} 423 424Return to [link boost_asio.tutorial.tuttimer3 Timer.3 - Binding arguments to a handler] 425 426[endsect] 427 428[endsect] 429 430[section:tuttimer4 Timer.4 - Using a member function as a handler] 431 432In this tutorial we will see how to use a class member function as a callback handler. The program should execute identically to the tutorial program from tutorial Timer.3. 433 434 435 436 437 438 439 ``''''''``#include <iostream> 440 ``''''''``#include <boost/asio.hpp> 441 ``''''''``#include <boost/bind/bind.hpp> 442 443 444 445Instead of defining a free function `print` as the callback handler, as we did in the earlier tutorial programs, we now define a class called `printer`. 446 447 448 449 ``''''''``class printer 450 ``''''''``{ 451 ``''''''``public: 452 453 454 455The constructor of this class will take a reference to the io\_context object and use it when initialising the `timer_` member. The counter used to shut down the program is now also a member of the class. 456 457 458 ``''''''`` printer(boost::asio::io_context& io) 459 ``''''''`` : timer_(io, boost::asio::chrono::seconds(1)), 460 ``''''''`` count_(0) 461 ``''''''`` { 462 463 464 465The boost::bind() function works just as well with class member functions as with free functions. Since all non-static class member functions have an implicit `this` parameter, we need to bind `this` to the function. As in tutorial Timer.3, boost::bind() converts our callback handler (now a member function) into a function object that can be invoked as though it has the signature `void(const boost::system::error_code&)`. 466 467You will note that the boost::asio::placeholders::error placeholder is not specified here, as the `print` member function does not accept an error object as a parameter. 468 469 470 ``''''''`` timer_.async_wait(boost::bind(&printer::print, this)); 471 ``''''''`` } 472 473 474 475In the class destructor we will print out the final value of the counter. 476 477 478 479 ``''''''`` ~printer() 480 ``''''''`` { 481 ``''''''`` std::cout << "Final count is " << count_ << std::endl; 482 ``''''''`` } 483 484 485 486The `print` member function is very similar to the `print` function from tutorial Timer.3, except that it now operates on the class data members instead of having the timer and counter passed in as parameters. 487 488 489 490 ``''''''`` void print() 491 ``''''''`` { 492 ``''''''`` if (count_ < 5) 493 ``''''''`` { 494 ``''''''`` std::cout << count_ << std::endl; 495 ``''''''`` ++count_; 496 497 ``''''''`` timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1)); 498 ``''''''`` timer_.async_wait(boost::bind(&printer::print, this)); 499 ``''''''`` } 500 ``''''''`` } 501 502 ``''''''``private: 503 ``''''''`` boost::asio::steady_timer timer_; 504 ``''''''`` int count_; 505 ``''''''``}; 506 507 508 509The `main` function is much simpler than before, as it now declares a local `printer` object before running the io\_context as normal. 510 511 512 513 ``''''''``int main() 514 ``''''''``{ 515 ``''''''`` boost::asio::io_context io; 516 ``''''''`` printer p(io); 517 ``''''''`` io.run(); 518 519 ``''''''`` return 0; 520 ``''''''``} 521 522 523 524See the [link boost_asio.tutorial.tuttimer4.src full source listing] 525 526Return to the [link boost_asio.tutorial tutorial index] 527 528Previous: [link boost_asio.tutorial.tuttimer3 Timer.3 - Binding arguments to a handler] 529 530Next: [link boost_asio.tutorial.tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs] 531 532 533 534 535 536[section:src Source listing for Timer.4] 537 538 539 ``''''''``// 540 ``''''''``// timer.cpp 541 ``''''''``// ~~~~~~~~~ 542 ``''''''``// 543 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 544 ``''''''``// 545 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 546 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 547 ``''''''``// 548 549 ``''''''``#include <iostream> 550 ``''''''``#include <boost/asio.hpp> 551 ``''''''``#include <boost/bind/bind.hpp> 552 553 ``''''''``class printer 554 ``''''''``{ 555 ``''''''``public: 556 ``''''''`` printer(boost::asio::io_context& io) 557 ``''''''`` : timer_(io, boost::asio::chrono::seconds(1)), 558 ``''''''`` count_(0) 559 ``''''''`` { 560 ``''''''`` timer_.async_wait(boost::bind(&printer::print, this)); 561 ``''''''`` } 562 563 ``''''''`` ~printer() 564 ``''''''`` { 565 ``''''''`` std::cout << "Final count is " << count_ << std::endl; 566 ``''''''`` } 567 568 ``''''''`` void print() 569 ``''''''`` { 570 ``''''''`` if (count_ < 5) 571 ``''''''`` { 572 ``''''''`` std::cout << count_ << std::endl; 573 ``''''''`` ++count_; 574 575 ``''''''`` timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1)); 576 ``''''''`` timer_.async_wait(boost::bind(&printer::print, this)); 577 ``''''''`` } 578 ``''''''`` } 579 580 ``''''''``private: 581 ``''''''`` boost::asio::steady_timer timer_; 582 ``''''''`` int count_; 583 ``''''''``}; 584 585 ``''''''``int main() 586 ``''''''``{ 587 ``''''''`` boost::asio::io_context io; 588 ``''''''`` printer p(io); 589 ``''''''`` io.run(); 590 591 ``''''''`` return 0; 592 ``''''''``} 593 594Return to [link boost_asio.tutorial.tuttimer4 Timer.4 - Using a member function as a handler] 595 596[endsect] 597 598[endsect] 599 600[section:tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs] 601 602This tutorial demonstrates the use of the 603[link boost_asio.reference.strand strand] class template to synchronise callback handlers in a multithreaded program. 604 605The previous four tutorials avoided the issue of handler synchronisation by calling the [link boost_asio.reference.io_context.run io_context::run()] function from one thread only. As you already know, the asio library provides a guarantee that callback handlers will only be called from threads that are currently calling [link boost_asio.reference.io_context.run io_context::run()]. Consequently, calling [link boost_asio.reference.io_context.run io_context::run()] from only one thread ensures that callback handlers cannot run concurrently. 606 607The single threaded approach is usually the best place to start when developing applications using asio. The downside is the limitations it places on programs, particularly servers, including: 608 609* Poor responsiveness when handlers can take a long time to complete. 610 611 612* An inability to scale on multiprocessor systems. 613 614 615 616 617If you find yourself running into these limitations, an alternative approach is to have a pool of threads calling [link boost_asio.reference.io_context.run io_context::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. 618 619 620 621 622 623 624 ``''''''``#include <iostream> 625 ``''''''``#include <boost/asio.hpp> 626 ``''''''``#include <boost/thread/thread.hpp> 627 ``''''''``#include <boost/bind/bind.hpp> 628 629 630 631We start by defining a class called `printer`, similar to the class in the previous tutorial. This class will extend the previous tutorial by running two timers in parallel. 632 633 634 635 ``''''''``class printer 636 ``''''''``{ 637 ``''''''``public: 638 639 640 641In addition to initialising a pair of boost::asio::steady\_timer members, the constructor initialises the `strand_` member, an object of type boost::asio::strand<boost::asio::io\_context::executor\_type>. 642 643The 644[link boost_asio.reference.strand strand] class template is an executor adapter that guarantees that, for those handlers that are dispatched through it, an executing handler will be allowed to complete before the next one is started. This is guaranteed irrespective of the number of threads that are calling [link boost_asio.reference.io_context.run io_context::run()]. Of course, the handlers may still execute concurrently with other handlers that were not dispatched through an 645[link boost_asio.reference.strand strand], or were dispatched through a different 646[link boost_asio.reference.strand strand] object. 647 648 649 ``''''''`` printer(boost::asio::io_context& io) 650 ``''''''`` : strand_(boost::asio::make_strand(io)), 651 ``''''''`` timer1_(io, boost::asio::chrono::seconds(1)), 652 ``''''''`` timer2_(io, boost::asio::chrono::seconds(1)), 653 ``''''''`` count_(0) 654 ``''''''`` { 655 656 657 658When initiating the asynchronous operations, each callback handler is "bound" to an boost::asio::strand<boost::asio::io\_context::executor\_type> object. The boost::asio::bind\_executor() function returns a new handler that automatically dispatches its contained handler through the 659[link boost_asio.reference.strand strand] object. By binding the handlers to the same 660[link boost_asio.reference.strand strand], we are ensuring that they cannot execute concurrently. 661 662 663 ``''''''`` timer1_.async_wait(boost::asio::bind_executor(strand_, 664 ``''''''`` boost::bind(&printer::print1, this))); 665 666 ``''''''`` timer2_.async_wait(boost::asio::bind_executor(strand_, 667 ``''''''`` boost::bind(&printer::print2, this))); 668 ``''''''`` } 669 670 ``''''''`` ~printer() 671 ``''''''`` { 672 ``''''''`` std::cout << "Final count is " << count_ << std::endl; 673 ``''''''`` } 674 675 676 677In 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. 678 679 680 681 ``''''''`` void print1() 682 ``''''''`` { 683 ``''''''`` if (count_ < 10) 684 ``''''''`` { 685 ``''''''`` std::cout << "Timer 1: " << count_ << std::endl; 686 ``''''''`` ++count_; 687 688 ``''''''`` timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1)); 689 690 ``''''''`` timer1_.async_wait(boost::asio::bind_executor(strand_, 691 ``''''''`` boost::bind(&printer::print1, this))); 692 ``''''''`` } 693 ``''''''`` } 694 695 ``''''''`` void print2() 696 ``''''''`` { 697 ``''''''`` if (count_ < 10) 698 ``''''''`` { 699 ``''''''`` std::cout << "Timer 2: " << count_ << std::endl; 700 ``''''''`` ++count_; 701 702 ``''''''`` timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1)); 703 704 ``''''''`` timer2_.async_wait(boost::asio::bind_executor(strand_, 705 ``''''''`` boost::bind(&printer::print2, this))); 706 ``''''''`` } 707 ``''''''`` } 708 709 ``''''''``private: 710 ``''''''`` boost::asio::strand<boost::asio::io_context::executor_type> strand_; 711 ``''''''`` boost::asio::steady_timer timer1_; 712 ``''''''`` boost::asio::steady_timer timer2_; 713 ``''''''`` int count_; 714 ``''''''``}; 715 716 717 718The `main` function now causes [link boost_asio.reference.io_context.run io_context::run()] to be called from two threads: the main thread and one additional thread. This is accomplished using an boost::thread object. 719 720Just as it would with a call from a single thread, concurrent calls to [link boost_asio.reference.io_context.run io_context::run()] will continue to execute while there is "work" left to do. The background thread will not exit until all asynchronous operations have completed. 721 722 723 724 ``''''''``int main() 725 ``''''''``{ 726 ``''''''`` boost::asio::io_context io; 727 ``''''''`` printer p(io); 728 ``''''''`` boost::thread t(boost::bind(&boost::asio::io_context::run, &io)); 729 ``''''''`` io.run(); 730 ``''''''`` t.join(); 731 732 ``''''''`` return 0; 733 ``''''''``} 734 735 736 737See the [link boost_asio.tutorial.tuttimer5.src full source listing] 738 739Return to the [link boost_asio.tutorial tutorial index] 740 741Previous: [link boost_asio.tutorial.tuttimer4 Timer.4 - Using a member function as a handler] 742 743 744 745 746 747[section:src Source listing for Timer.5] 748 749 750 ``''''''``// 751 ``''''''``// timer.cpp 752 ``''''''``// ~~~~~~~~~ 753 ``''''''``// 754 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 755 ``''''''``// 756 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 757 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 758 ``''''''``// 759 760 ``''''''``#include <iostream> 761 ``''''''``#include <boost/asio.hpp> 762 ``''''''``#include <boost/thread/thread.hpp> 763 ``''''''``#include <boost/bind/bind.hpp> 764 765 ``''''''``class printer 766 ``''''''``{ 767 ``''''''``public: 768 ``''''''`` printer(boost::asio::io_context& io) 769 ``''''''`` : strand_(boost::asio::make_strand(io)), 770 ``''''''`` timer1_(io, boost::asio::chrono::seconds(1)), 771 ``''''''`` timer2_(io, boost::asio::chrono::seconds(1)), 772 ``''''''`` count_(0) 773 ``''''''`` { 774 ``''''''`` timer1_.async_wait(boost::asio::bind_executor(strand_, 775 ``''''''`` boost::bind(&printer::print1, this))); 776 777 ``''''''`` timer2_.async_wait(boost::asio::bind_executor(strand_, 778 ``''''''`` boost::bind(&printer::print2, this))); 779 ``''''''`` } 780 781 ``''''''`` ~printer() 782 ``''''''`` { 783 ``''''''`` std::cout << "Final count is " << count_ << std::endl; 784 ``''''''`` } 785 786 ``''''''`` void print1() 787 ``''''''`` { 788 ``''''''`` if (count_ < 10) 789 ``''''''`` { 790 ``''''''`` std::cout << "Timer 1: " << count_ << std::endl; 791 ``''''''`` ++count_; 792 793 ``''''''`` timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1)); 794 795 ``''''''`` timer1_.async_wait(boost::asio::bind_executor(strand_, 796 ``''''''`` boost::bind(&printer::print1, this))); 797 ``''''''`` } 798 ``''''''`` } 799 800 ``''''''`` void print2() 801 ``''''''`` { 802 ``''''''`` if (count_ < 10) 803 ``''''''`` { 804 ``''''''`` std::cout << "Timer 2: " << count_ << std::endl; 805 ``''''''`` ++count_; 806 807 ``''''''`` timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1)); 808 809 ``''''''`` timer2_.async_wait(boost::asio::bind_executor(strand_, 810 ``''''''`` boost::bind(&printer::print2, this))); 811 ``''''''`` } 812 ``''''''`` } 813 814 ``''''''``private: 815 ``''''''`` boost::asio::strand<boost::asio::io_context::executor_type> strand_; 816 ``''''''`` boost::asio::steady_timer timer1_; 817 ``''''''`` boost::asio::steady_timer timer2_; 818 ``''''''`` int count_; 819 ``''''''``}; 820 821 ``''''''``int main() 822 ``''''''``{ 823 ``''''''`` boost::asio::io_context io; 824 ``''''''`` printer p(io); 825 ``''''''`` boost::thread t(boost::bind(&boost::asio::io_context::run, &io)); 826 ``''''''`` io.run(); 827 ``''''''`` t.join(); 828 829 ``''''''`` return 0; 830 ``''''''``} 831 832Return to [link boost_asio.tutorial.tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs] 833 834[endsect] 835 836[endsect] 837 838[section:tutdaytime1 Daytime.1 - A synchronous TCP daytime client] 839 840This tutorial program shows how to use asio to implement a client application with TCP. 841 842 843 844 845 846We start by including the necessary header files. 847 848 849 ``''''''``#include <iostream> 850 ``''''''``#include <boost/array.hpp> 851 ``''''''``#include <boost/asio.hpp> 852 853 854 855The purpose of this application is to access a daytime service, so we need the user to specify the server. 856 857 858 859 ``''''''``using boost::asio::ip::tcp; 860 861 ``''''''``int main(int argc, char* argv[]) 862 ``''''''``{ 863 ``''''''`` try 864 ``''''''`` { 865 ``''''''`` if (argc != 2) 866 ``''''''`` { 867 ``''''''`` std::cerr << "Usage: client <host>" << std::endl; 868 ``''''''`` return 1; 869 ``''''''`` } 870 871 872 873All programs that use asio need to have at least one I/O execution context, such as an 874[link boost_asio.reference.io_context io_context] object. 875 876 877 878 ``''''''`` boost::asio::io_context io_context; 879 880 881 882We 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 [link boost_asio.reference.ip__tcp.resolver ip::tcp::resolver] object. 883 884 885 886 ``''''''`` tcp::resolver resolver(io_context); 887 888 889 890A resolver takes a host name and service name and turns them into a list of endpoints. We perform a resolve call using the name of the server, specified in `argv[1]`, and the name of the service, in this case `"daytime"`. 891 892The list of endpoints is returned using an object of type [link boost_asio.reference.ip__basic_resolver.results_type ip::tcp::resolver::results_type]. This object is a range, with begin() and end() member functions that may be used for iterating over the results. 893 894 895 ``''''''`` tcp::resolver::results_type endpoints = 896 ``''''''`` resolver.resolve(argv[1], "daytime"); 897 898 899 900Now 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. The boost::asio::connect() function does this for us automatically. 901 902 903 904 ``''''''`` tcp::socket socket(io_context); 905 ``''''''`` boost::asio::connect(socket, endpoints); 906 907 908 909The connection is open. All we need to do now is read the response from the daytime service. 910 911We 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`. 912 913 914 915 ``''''''`` for (;;) 916 ``''''''`` { 917 ``''''''`` boost::array<char, 128> buf; 918 ``''''''`` boost::system::error_code error; 919 920 ``''''''`` size_t len = socket.read_some(boost::asio::buffer(buf), error); 921 922 923 924When the server closes the connection, the [link boost_asio.reference.basic_stream_socket.read_some ip::tcp::socket::read_some()] function will exit with the boost::asio::error::eof error, which is how we know to exit the loop. 925 926 927 928 ``''''''`` if (error == boost::asio::error::eof) 929 ``''''''`` break; // Connection closed cleanly by peer. 930 ``''''''`` else if (error) 931 ``''''''`` throw boost::system::system_error(error); // Some other error. 932 933 ``''''''`` std::cout.write(buf.data(), len); 934 ``''''''`` } 935 936 937 938Finally, handle any exceptions that may have been thrown. 939 940 941 ``''''''`` } 942 ``''''''`` catch (std::exception& e) 943 ``''''''`` { 944 ``''''''`` std::cerr << e.what() << std::endl; 945 ``''''''`` } 946 947 948 949See the [link boost_asio.tutorial.tutdaytime1.src full source listing] 950 951Return to the [link boost_asio.tutorial tutorial index] 952 953Next: [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server] 954 955 956 957[section:src Source listing for Daytime.1] 958 959 960 ``''''''``// 961 ``''''''``// client.cpp 962 ``''''''``// ~~~~~~~~~~ 963 ``''''''``// 964 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 965 ``''''''``// 966 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 967 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 968 ``''''''``// 969 970 ``''''''``#include <iostream> 971 ``''''''``#include <boost/array.hpp> 972 ``''''''``#include <boost/asio.hpp> 973 974 ``''''''``using boost::asio::ip::tcp; 975 976 ``''''''``int main(int argc, char* argv[]) 977 ``''''''``{ 978 ``''''''`` try 979 ``''''''`` { 980 ``''''''`` if (argc != 2) 981 ``''''''`` { 982 ``''''''`` std::cerr << "Usage: client <host>" << std::endl; 983 ``''''''`` return 1; 984 ``''''''`` } 985 986 ``''''''`` boost::asio::io_context io_context; 987 988 ``''''''`` tcp::resolver resolver(io_context); 989 ``''''''`` tcp::resolver::results_type endpoints = 990 ``''''''`` resolver.resolve(argv[1], "daytime"); 991 992 ``''''''`` tcp::socket socket(io_context); 993 ``''''''`` boost::asio::connect(socket, endpoints); 994 995 ``''''''`` for (;;) 996 ``''''''`` { 997 ``''''''`` boost::array<char, 128> buf; 998 ``''''''`` boost::system::error_code error; 999 1000 ``''''''`` size_t len = socket.read_some(boost::asio::buffer(buf), error); 1001 1002 ``''''''`` if (error == boost::asio::error::eof) 1003 ``''''''`` break; // Connection closed cleanly by peer. 1004 ``''''''`` else if (error) 1005 ``''''''`` throw boost::system::system_error(error); // Some other error. 1006 1007 ``''''''`` std::cout.write(buf.data(), len); 1008 ``''''''`` } 1009 ``''''''`` } 1010 ``''''''`` catch (std::exception& e) 1011 ``''''''`` { 1012 ``''''''`` std::cerr << e.what() << std::endl; 1013 ``''''''`` } 1014 1015 ``''''''`` return 0; 1016 ``''''''``} 1017 1018Return to [link boost_asio.tutorial.tutdaytime1 Daytime.1 - A synchronous TCP daytime client] 1019 1020[endsect] 1021 1022[endsect] 1023 1024[section:tutdaytime2 Daytime.2 - A synchronous TCP daytime server] 1025 1026This tutorial program shows how to use asio to implement a server application with TCP. 1027 1028 1029 1030 1031 1032 1033 ``''''''``#include <ctime> 1034 ``''''''``#include <iostream> 1035 ``''''''``#include <string> 1036 ``''''''``#include <boost/asio.hpp> 1037 1038 ``''''''``using boost::asio::ip::tcp; 1039 1040 1041 1042We 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. 1043 1044 1045 1046 ``''''''``std::string make_daytime_string() 1047 ``''''''``{ 1048 ``''''''`` using namespace std; // For time_t, time and ctime; 1049 ``''''''`` time_t now = time(0); 1050 ``''''''`` return ctime(&now); 1051 ``''''''``} 1052 1053 ``''''''``int main() 1054 ``''''''``{ 1055 ``''''''`` try 1056 ``''''''`` { 1057 ``''''''`` boost::asio::io_context io_context; 1058 1059 1060 1061A [link boost_asio.reference.ip__tcp.acceptor 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. 1062 1063 1064 1065 ``''''''`` tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13)); 1066 1067 1068 1069This 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. 1070 1071 1072 1073 ``''''''`` for (;;) 1074 ``''''''`` { 1075 ``''''''`` tcp::socket socket(io_context); 1076 ``''''''`` acceptor.accept(socket); 1077 1078 1079 1080A client is accessing our service. Determine the current time and transfer this information to the client. 1081 1082 1083 1084 ``''''''`` std::string message = make_daytime_string(); 1085 1086 ``''''''`` boost::system::error_code ignored_error; 1087 ``''''''`` boost::asio::write(socket, boost::asio::buffer(message), ignored_error); 1088 ``''''''`` } 1089 ``''''''`` } 1090 1091 1092 1093Finally, handle any exceptions. 1094 1095 1096 ``''''''`` catch (std::exception& e) 1097 ``''''''`` { 1098 ``''''''`` std::cerr << e.what() << std::endl; 1099 ``''''''`` } 1100 1101 ``''''''`` return 0; 1102 ``''''''``} 1103 1104 1105 1106See the [link boost_asio.tutorial.tutdaytime2.src full source listing] 1107 1108Return to the [link boost_asio.tutorial tutorial index] 1109 1110Previous: [link boost_asio.tutorial.tutdaytime1 Daytime.1 - A synchronous TCP daytime client] 1111 1112Next: [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server] 1113 1114 1115 1116[section:src Source listing for Daytime.2] 1117 1118 1119 ``''''''``// 1120 ``''''''``// server.cpp 1121 ``''''''``// ~~~~~~~~~~ 1122 ``''''''``// 1123 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 1124 ``''''''``// 1125 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 1126 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 1127 ``''''''``// 1128 1129 ``''''''``#include <ctime> 1130 ``''''''``#include <iostream> 1131 ``''''''``#include <string> 1132 ``''''''``#include <boost/asio.hpp> 1133 1134 ``''''''``using boost::asio::ip::tcp; 1135 1136 ``''''''``std::string make_daytime_string() 1137 ``''''''``{ 1138 ``''''''`` using namespace std; // For time_t, time and ctime; 1139 ``''''''`` time_t now = time(0); 1140 ``''''''`` return ctime(&now); 1141 ``''''''``} 1142 1143 ``''''''``int main() 1144 ``''''''``{ 1145 ``''''''`` try 1146 ``''''''`` { 1147 ``''''''`` boost::asio::io_context io_context; 1148 1149 ``''''''`` tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13)); 1150 1151 ``''''''`` for (;;) 1152 ``''''''`` { 1153 ``''''''`` tcp::socket socket(io_context); 1154 ``''''''`` acceptor.accept(socket); 1155 1156 ``''''''`` std::string message = make_daytime_string(); 1157 1158 ``''''''`` boost::system::error_code ignored_error; 1159 ``''''''`` boost::asio::write(socket, boost::asio::buffer(message), ignored_error); 1160 ``''''''`` } 1161 ``''''''`` } 1162 ``''''''`` catch (std::exception& e) 1163 ``''''''`` { 1164 ``''''''`` std::cerr << e.what() << std::endl; 1165 ``''''''`` } 1166 1167 ``''''''`` return 0; 1168 ``''''''``} 1169 1170Return to [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server] 1171 1172[endsect] 1173 1174[endsect] 1175 1176[section:tutdaytime3 Daytime.3 - An asynchronous TCP daytime server] 1177 1178[heading The main() function] 1179 1180 1181 ``''''''``int main() 1182 ``''''''``{ 1183 ``''''''`` try 1184 ``''''''`` { 1185 1186 1187 1188We need to create a server object to accept incoming client connections. The 1189[link boost_asio.reference.io_context io_context] object provides I/O services, such as sockets, that the server object will use. 1190 1191 1192 ``''''''`` boost::asio::io_context io_context; 1193 ``''''''`` tcp_server server(io_context); 1194 1195 1196 1197Run the 1198[link boost_asio.reference.io_context io_context] object so that it will perform asynchronous operations on your behalf. 1199 1200 1201 ``''''''`` io_context.run(); 1202 ``''''''`` } 1203 ``''''''`` catch (std::exception& e) 1204 ``''''''`` { 1205 ``''''''`` std::cerr << e.what() << std::endl; 1206 ``''''''`` } 1207 1208 ``''''''`` return 0; 1209 ``''''''``} 1210 1211 1212 1213[heading The tcp_server class] 1214 1215 1216 ``''''''``class tcp_server 1217 ``''''''``{ 1218 ``''''''``public: 1219 1220 1221 1222The constructor initialises an acceptor to listen on TCP port 13. 1223 1224 1225 ``''''''`` tcp_server(boost::asio::io_context& io_context) 1226 ``''''''`` : io_context_(io_context), 1227 ``''''''`` acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) 1228 ``''''''`` { 1229 ``''''''`` start_accept(); 1230 ``''''''`` } 1231 1232 ``''''''``private: 1233 1234 1235 1236The function `start_accept()` creates a socket and initiates an asynchronous accept operation to wait for a new connection. 1237 1238 1239 ``''''''`` void start_accept() 1240 ``''''''`` { 1241 ``''''''`` tcp_connection::pointer new_connection = 1242 ``''''''`` tcp_connection::create(io_context_); 1243 1244 ``''''''`` acceptor_.async_accept(new_connection->socket(), 1245 ``''''''`` boost::bind(&tcp_server::handle_accept, this, new_connection, 1246 ``''''''`` boost::asio::placeholders::error)); 1247 ``''''''`` } 1248 1249 1250 1251The 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. 1252 1253 1254 1255 ``''''''`` void handle_accept(tcp_connection::pointer new_connection, 1256 ``''''''`` const boost::system::error_code& error) 1257 ``''''''`` { 1258 ``''''''`` if (!error) 1259 ``''''''`` { 1260 ``''''''`` new_connection->start(); 1261 ``''''''`` } 1262 1263 ``''''''`` start_accept(); 1264 ``''''''`` } 1265 1266 1267 1268[heading The tcp_connection class] 1269 1270We 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. 1271 1272 1273 ``''''''``class tcp_connection 1274 ``''''''`` : public boost::enable_shared_from_this<tcp_connection> 1275 ``''''''``{ 1276 ``''''''``public: 1277 ``''''''`` typedef boost::shared_ptr<tcp_connection> pointer; 1278 1279 ``''''''`` static pointer create(boost::asio::io_context& io_context) 1280 ``''''''`` { 1281 ``''''''`` return pointer(new tcp_connection(io_context)); 1282 ``''''''`` } 1283 1284 ``''''''`` tcp::socket& socket() 1285 ``''''''`` { 1286 ``''''''`` return socket_; 1287 ``''''''`` } 1288 1289 1290 1291In 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 [link boost_asio.reference.basic_stream_socket.async_write_some ip::tcp::socket::async_write_some()], to ensure that the entire block of data is sent. 1292 1293 1294 1295 ``''''''`` void start() 1296 ``''''''`` { 1297 1298 1299 1300The 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. 1301 1302 1303 ``''''''`` message_ = make_daytime_string(); 1304 1305 1306 1307When 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()`. 1308 1309 1310 1311 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_), 1312 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this(), 1313 ``''''''`` boost::asio::placeholders::error, 1314 ``''''''`` boost::asio::placeholders::bytes_transferred)); 1315 1316 1317 1318Any further actions for this client connection are now the responsibility of `handle_write()`. 1319 1320 1321 ``''''''`` } 1322 1323 ``''''''``private: 1324 ``''''''`` tcp_connection(boost::asio::io_context& io_context) 1325 ``''''''`` : socket_(io_context) 1326 ``''''''`` { 1327 ``''''''`` } 1328 1329 ``''''''`` void handle_write(const boost::system::error_code& /*error*/, 1330 ``''''''`` size_t /*bytes_transferred*/) 1331 ``''''''`` { 1332 ``''''''`` } 1333 1334 ``''''''`` tcp::socket socket_; 1335 ``''''''`` std::string message_; 1336 ``''''''``}; 1337 1338 1339 1340[heading Removing unused handler parameters] 1341 1342You 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: 1343 1344 1345 ``''''''`` void handle_write() 1346 ``''''''`` { 1347 ``''''''`` } 1348 1349 1350 1351The boost::asio::async\_write() call used to initiate the call can then be changed to just: 1352 1353 1354 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_), 1355 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this())); 1356 1357 1358 1359See the [link boost_asio.tutorial.tutdaytime3.src full source listing] 1360 1361Return to the [link boost_asio.tutorial tutorial index] 1362 1363Previous: [link boost_asio.tutorial.tutdaytime2 Daytime.2 - A synchronous TCP daytime server] 1364 1365Next: [link boost_asio.tutorial.tutdaytime4 Daytime.4 - A synchronous UDP daytime client] 1366 1367 1368 1369[section:src Source listing for Daytime.3] 1370 1371 1372 ``''''''``// 1373 ``''''''``// server.cpp 1374 ``''''''``// ~~~~~~~~~~ 1375 ``''''''``// 1376 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 1377 ``''''''``// 1378 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 1379 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 1380 ``''''''``// 1381 1382 ``''''''``#include <ctime> 1383 ``''''''``#include <iostream> 1384 ``''''''``#include <string> 1385 ``''''''``#include <boost/bind/bind.hpp> 1386 ``''''''``#include <boost/shared_ptr.hpp> 1387 ``''''''``#include <boost/enable_shared_from_this.hpp> 1388 ``''''''``#include <boost/asio.hpp> 1389 1390 ``''''''``using boost::asio::ip::tcp; 1391 1392 ``''''''``std::string make_daytime_string() 1393 ``''''''``{ 1394 ``''''''`` using namespace std; // For time_t, time and ctime; 1395 ``''''''`` time_t now = time(0); 1396 ``''''''`` return ctime(&now); 1397 ``''''''``} 1398 1399 ``''''''``class tcp_connection 1400 ``''''''`` : public boost::enable_shared_from_this<tcp_connection> 1401 ``''''''``{ 1402 ``''''''``public: 1403 ``''''''`` typedef boost::shared_ptr<tcp_connection> pointer; 1404 1405 ``''''''`` static pointer create(boost::asio::io_context& io_context) 1406 ``''''''`` { 1407 ``''''''`` return pointer(new tcp_connection(io_context)); 1408 ``''''''`` } 1409 1410 ``''''''`` tcp::socket& socket() 1411 ``''''''`` { 1412 ``''''''`` return socket_; 1413 ``''''''`` } 1414 1415 ``''''''`` void start() 1416 ``''''''`` { 1417 ``''''''`` message_ = make_daytime_string(); 1418 1419 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_), 1420 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this(), 1421 ``''''''`` boost::asio::placeholders::error, 1422 ``''''''`` boost::asio::placeholders::bytes_transferred)); 1423 ``''''''`` } 1424 1425 ``''''''``private: 1426 ``''''''`` tcp_connection(boost::asio::io_context& io_context) 1427 ``''''''`` : socket_(io_context) 1428 ``''''''`` { 1429 ``''''''`` } 1430 1431 ``''''''`` void handle_write(const boost::system::error_code& /*error*/, 1432 ``''''''`` size_t /*bytes_transferred*/) 1433 ``''''''`` { 1434 ``''''''`` } 1435 1436 ``''''''`` tcp::socket socket_; 1437 ``''''''`` std::string message_; 1438 ``''''''``}; 1439 1440 ``''''''``class tcp_server 1441 ``''''''``{ 1442 ``''''''``public: 1443 ``''''''`` tcp_server(boost::asio::io_context& io_context) 1444 ``''''''`` : io_context_(io_context), 1445 ``''''''`` acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) 1446 ``''''''`` { 1447 ``''''''`` start_accept(); 1448 ``''''''`` } 1449 1450 ``''''''``private: 1451 ``''''''`` void start_accept() 1452 ``''''''`` { 1453 ``''''''`` tcp_connection::pointer new_connection = 1454 ``''''''`` tcp_connection::create(io_context_); 1455 1456 ``''''''`` acceptor_.async_accept(new_connection->socket(), 1457 ``''''''`` boost::bind(&tcp_server::handle_accept, this, new_connection, 1458 ``''''''`` boost::asio::placeholders::error)); 1459 ``''''''`` } 1460 1461 ``''''''`` void handle_accept(tcp_connection::pointer new_connection, 1462 ``''''''`` const boost::system::error_code& error) 1463 ``''''''`` { 1464 ``''''''`` if (!error) 1465 ``''''''`` { 1466 ``''''''`` new_connection->start(); 1467 ``''''''`` } 1468 1469 ``''''''`` start_accept(); 1470 ``''''''`` } 1471 1472 ``''''''`` boost::asio::io_context& io_context_; 1473 ``''''''`` tcp::acceptor acceptor_; 1474 ``''''''``}; 1475 1476 ``''''''``int main() 1477 ``''''''``{ 1478 ``''''''`` try 1479 ``''''''`` { 1480 ``''''''`` boost::asio::io_context io_context; 1481 ``''''''`` tcp_server server(io_context); 1482 ``''''''`` io_context.run(); 1483 ``''''''`` } 1484 ``''''''`` catch (std::exception& e) 1485 ``''''''`` { 1486 ``''''''`` std::cerr << e.what() << std::endl; 1487 ``''''''`` } 1488 1489 ``''''''`` return 0; 1490 ``''''''``} 1491 1492Return to [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server] 1493 1494[endsect] 1495 1496[endsect] 1497 1498[section:tutdaytime4 Daytime.4 - A synchronous UDP daytime client] 1499 1500This tutorial program shows how to use asio to implement a client application with UDP. 1501 1502 1503 ``''''''``#include <iostream> 1504 ``''''''``#include <boost/array.hpp> 1505 ``''''''``#include <boost/asio.hpp> 1506 1507 ``''''''``using boost::asio::ip::udp; 1508 1509 1510 1511The start of the application is essentially the same as for the TCP daytime client. 1512 1513 1514 1515 ``''''''``int main(int argc, char* argv[]) 1516 ``''''''``{ 1517 ``''''''`` try 1518 ``''''''`` { 1519 ``''''''`` if (argc != 2) 1520 ``''''''`` { 1521 ``''''''`` std::cerr << "Usage: client <host>" << std::endl; 1522 ``''''''`` return 1; 1523 ``''''''`` } 1524 1525 ``''''''`` boost::asio::io_context io_context; 1526 1527 1528 1529We use an [link boost_asio.reference.ip__udp.resolver 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 [link boost_asio.reference.ip__udp.v4 ip::udp::v4()] argument. 1530 1531 1532 1533 ``''''''`` udp::resolver resolver(io_context); 1534 ``''''''`` udp::endpoint receiver_endpoint = 1535 ``''''''`` *resolver.resolve(udp::v4(), argv[1], "daytime").begin(); 1536 1537 1538 1539The [link boost_asio.reference.ip__basic_resolver.resolve 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. 1540 1541Since UDP is datagram-oriented, we will not be using a stream socket. Create an [link boost_asio.reference.ip__udp.socket ip::udp::socket] and initiate contact with the remote endpoint. 1542 1543 1544 1545 ``''''''`` udp::socket socket(io_context); 1546 ``''''''`` socket.open(udp::v4()); 1547 1548 ``''''''`` boost::array<char, 1> send_buf = {{ 0 }}; 1549 ``''''''`` socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint); 1550 1551 1552 1553Now 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 [link boost_asio.reference.basic_datagram_socket.receive_from ip::udp::socket::receive_from()]. 1554 1555 1556 1557 ``''''''`` boost::array<char, 128> recv_buf; 1558 ``''''''`` udp::endpoint sender_endpoint; 1559 ``''''''`` size_t len = socket.receive_from( 1560 ``''''''`` boost::asio::buffer(recv_buf), sender_endpoint); 1561 1562 ``''''''`` std::cout.write(recv_buf.data(), len); 1563 ``''''''`` } 1564 1565 1566 1567Finally, handle any exceptions that may have been thrown. 1568 1569 1570 ``''''''`` catch (std::exception& e) 1571 ``''''''`` { 1572 ``''''''`` std::cerr << e.what() << std::endl; 1573 ``''''''`` } 1574 1575 ``''''''`` return 0; 1576 ``''''''``} 1577 1578See the [link boost_asio.tutorial.tutdaytime4.src full source listing] 1579 1580Return to the [link boost_asio.tutorial tutorial index] 1581 1582Previous: [link boost_asio.tutorial.tutdaytime3 Daytime.3 - An asynchronous TCP daytime server] 1583 1584Next: [link boost_asio.tutorial.tutdaytime5 Daytime.5 - A synchronous UDP daytime server] 1585 1586 1587 1588[section:src Source listing for Daytime.4] 1589 1590 1591 ``''''''``// 1592 ``''''''``// client.cpp 1593 ``''''''``// ~~~~~~~~~~ 1594 ``''''''``// 1595 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 1596 ``''''''``// 1597 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 1598 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 1599 ``''''''``// 1600 1601 ``''''''``#include <iostream> 1602 ``''''''``#include <boost/array.hpp> 1603 ``''''''``#include <boost/asio.hpp> 1604 1605 ``''''''``using boost::asio::ip::udp; 1606 1607 ``''''''``int main(int argc, char* argv[]) 1608 ``''''''``{ 1609 ``''''''`` try 1610 ``''''''`` { 1611 ``''''''`` if (argc != 2) 1612 ``''''''`` { 1613 ``''''''`` std::cerr << "Usage: client <host>" << std::endl; 1614 ``''''''`` return 1; 1615 ``''''''`` } 1616 1617 ``''''''`` boost::asio::io_context io_context; 1618 1619 ``''''''`` udp::resolver resolver(io_context); 1620 ``''''''`` udp::endpoint receiver_endpoint = 1621 ``''''''`` *resolver.resolve(udp::v4(), argv[1], "daytime").begin(); 1622 1623 ``''''''`` udp::socket socket(io_context); 1624 ``''''''`` socket.open(udp::v4()); 1625 1626 ``''''''`` boost::array<char, 1> send_buf = {{ 0 }}; 1627 ``''''''`` socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint); 1628 1629 ``''''''`` boost::array<char, 128> recv_buf; 1630 ``''''''`` udp::endpoint sender_endpoint; 1631 ``''''''`` size_t len = socket.receive_from( 1632 ``''''''`` boost::asio::buffer(recv_buf), sender_endpoint); 1633 1634 ``''''''`` std::cout.write(recv_buf.data(), len); 1635 ``''''''`` } 1636 ``''''''`` catch (std::exception& e) 1637 ``''''''`` { 1638 ``''''''`` std::cerr << e.what() << std::endl; 1639 ``''''''`` } 1640 1641 ``''''''`` return 0; 1642 ``''''''``} 1643 1644Return to [link boost_asio.tutorial.tutdaytime4 Daytime.4 - A synchronous UDP daytime client] 1645 1646[endsect] 1647 1648[endsect] 1649 1650[section:tutdaytime5 Daytime.5 - A synchronous UDP daytime server] 1651 1652This tutorial program shows how to use asio to implement a server application with UDP. 1653 1654 1655 ``''''''``int main() 1656 ``''''''``{ 1657 ``''''''`` try 1658 ``''''''`` { 1659 ``''''''`` boost::asio::io_context io_context; 1660 1661 1662 1663Create an [link boost_asio.reference.ip__udp.socket ip::udp::socket] object to receive requests on UDP port 13. 1664 1665 1666 1667 ``''''''`` udp::socket socket(io_context, udp::endpoint(udp::v4(), 13)); 1668 1669 1670 1671Wait for a client to initiate contact with us. The remote\_endpoint object will be populated by [link boost_asio.reference.basic_datagram_socket.receive_from ip::udp::socket::receive_from()]. 1672 1673 1674 1675 ``''''''`` for (;;) 1676 ``''''''`` { 1677 ``''''''`` boost::array<char, 1> recv_buf; 1678 ``''''''`` udp::endpoint remote_endpoint; 1679 ``''''''`` socket.receive_from(boost::asio::buffer(recv_buf), remote_endpoint); 1680 1681 1682 1683Determine what we are going to send back to the client. 1684 1685 1686 1687 ``''''''`` std::string message = make_daytime_string(); 1688 1689 1690 1691Send the response to the remote\_endpoint. 1692 1693 1694 1695 ``''''''`` boost::system::error_code ignored_error; 1696 ``''''''`` socket.send_to(boost::asio::buffer(message), 1697 ``''''''`` remote_endpoint, 0, ignored_error); 1698 ``''''''`` } 1699 ``''''''`` } 1700 1701 1702 1703Finally, handle any exceptions. 1704 1705 1706 ``''''''`` catch (std::exception& e) 1707 ``''''''`` { 1708 ``''''''`` std::cerr << e.what() << std::endl; 1709 ``''''''`` } 1710 1711 ``''''''`` return 0; 1712 ``''''''``} 1713 1714 1715 1716See the [link boost_asio.tutorial.tutdaytime5.src full source listing] 1717 1718Return to the [link boost_asio.tutorial tutorial index] 1719 1720Previous: [link boost_asio.tutorial.tutdaytime4 Daytime.4 - A synchronous UDP daytime client] 1721 1722Next: [link boost_asio.tutorial.tutdaytime6 Daytime.6 - An asynchronous UDP daytime server] 1723 1724 1725 1726[section:src Source listing for Daytime.5] 1727 1728 1729 ``''''''``// 1730 ``''''''``// server.cpp 1731 ``''''''``// ~~~~~~~~~~ 1732 ``''''''``// 1733 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 1734 ``''''''``// 1735 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 1736 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 1737 ``''''''``// 1738 1739 ``''''''``#include <ctime> 1740 ``''''''``#include <iostream> 1741 ``''''''``#include <string> 1742 ``''''''``#include <boost/array.hpp> 1743 ``''''''``#include <boost/asio.hpp> 1744 1745 ``''''''``using boost::asio::ip::udp; 1746 1747 ``''''''``std::string make_daytime_string() 1748 ``''''''``{ 1749 ``''''''`` using namespace std; // For time_t, time and ctime; 1750 ``''''''`` time_t now = time(0); 1751 ``''''''`` return ctime(&now); 1752 ``''''''``} 1753 1754 ``''''''``int main() 1755 ``''''''``{ 1756 ``''''''`` try 1757 ``''''''`` { 1758 ``''''''`` boost::asio::io_context io_context; 1759 1760 ``''''''`` udp::socket socket(io_context, udp::endpoint(udp::v4(), 13)); 1761 1762 ``''''''`` for (;;) 1763 ``''''''`` { 1764 ``''''''`` boost::array<char, 1> recv_buf; 1765 ``''''''`` udp::endpoint remote_endpoint; 1766 ``''''''`` socket.receive_from(boost::asio::buffer(recv_buf), remote_endpoint); 1767 1768 ``''''''`` std::string message = make_daytime_string(); 1769 1770 ``''''''`` boost::system::error_code ignored_error; 1771 ``''''''`` socket.send_to(boost::asio::buffer(message), 1772 ``''''''`` remote_endpoint, 0, ignored_error); 1773 ``''''''`` } 1774 ``''''''`` } 1775 ``''''''`` catch (std::exception& e) 1776 ``''''''`` { 1777 ``''''''`` std::cerr << e.what() << std::endl; 1778 ``''''''`` } 1779 1780 ``''''''`` return 0; 1781 ``''''''``} 1782 1783Return to [link boost_asio.tutorial.tutdaytime5 Daytime.5 - A synchronous UDP daytime server] 1784 1785[endsect] 1786 1787[endsect] 1788 1789[section:tutdaytime6 Daytime.6 - An asynchronous UDP daytime server] 1790 1791[heading The main() function] 1792 1793 1794 ``''''''``int main() 1795 ``''''''``{ 1796 ``''''''`` try 1797 ``''''''`` { 1798 1799 1800 1801Create a server object to accept incoming client requests, and run the 1802[link boost_asio.reference.io_context io_context] object. 1803 1804 1805 ``''''''`` boost::asio::io_context io_context; 1806 ``''''''`` udp_server server(io_context); 1807 ``''''''`` io_context.run(); 1808 ``''''''`` } 1809 ``''''''`` catch (std::exception& e) 1810 ``''''''`` { 1811 ``''''''`` std::cerr << e.what() << std::endl; 1812 ``''''''`` } 1813 1814 ``''''''`` return 0; 1815 ``''''''``} 1816 1817 1818 1819[heading The udp_server class] 1820 1821 1822 ``''''''``class udp_server 1823 ``''''''``{ 1824 ``''''''``public: 1825 1826 1827 1828The constructor initialises a socket to listen on UDP port 13. 1829 1830 1831 ``''''''`` udp_server(boost::asio::io_context& io_context) 1832 ``''''''`` : socket_(io_context, udp::endpoint(udp::v4(), 13)) 1833 ``''''''`` { 1834 ``''''''`` start_receive(); 1835 ``''''''`` } 1836 1837 ``''''''``private: 1838 ``''''''`` void start_receive() 1839 ``''''''`` { 1840 1841 1842 1843The function [link boost_asio.reference.basic_datagram_socket.async_receive_from ip::udp::socket::async_receive_from()] will cause the application to listen in the background for a new request. When such a request is received, the 1844[link boost_asio.reference.io_context io_context] object will invoke the `handle_receive()` function with two arguments: a value of type boost::system::error\_code indicating whether the operation succeeded or failed, and a `size_t` value `bytes_transferred` specifying the number of bytes received. 1845 1846 1847 ``''''''`` socket_.async_receive_from( 1848 ``''''''`` boost::asio::buffer(recv_buffer_), remote_endpoint_, 1849 ``''''''`` boost::bind(&udp_server::handle_receive, this, 1850 ``''''''`` boost::asio::placeholders::error, 1851 ``''''''`` boost::asio::placeholders::bytes_transferred)); 1852 ``''''''`` } 1853 1854 1855 1856The function `handle_receive()` will service the client request. 1857 1858 1859 1860 ``''''''`` void handle_receive(const boost::system::error_code& error, 1861 ``''''''`` std::size_t /*bytes_transferred*/) 1862 ``''''''`` { 1863 1864 1865 1866The `error` parameter contains the result of the asynchronous operation. Since we only provide the 1-byte `recv_buffer_` to contain the client's request, the 1867[link boost_asio.reference.io_context io_context] object would return an error if the client sent anything larger. We can ignore such an error if it comes up. 1868 1869 1870 ``''''''`` if (!error) 1871 ``''''''`` { 1872 1873 1874 1875Determine what we are going to send. 1876 1877 1878 ``''''''`` boost::shared_ptr<std::string> message( 1879 ``''''''`` new std::string(make_daytime_string())); 1880 1881 1882 1883We now call [link boost_asio.reference.basic_datagram_socket.async_send_to ip::udp::socket::async_send_to()] to serve the data to the client. 1884 1885 1886 1887 ``''''''`` socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, 1888 ``''''''`` boost::bind(&udp_server::handle_send, this, message, 1889 ``''''''`` boost::asio::placeholders::error, 1890 ``''''''`` boost::asio::placeholders::bytes_transferred)); 1891 1892 1893 1894When 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. 1895 1896Start listening for the next client request. 1897 1898 1899 1900 ``''''''`` start_receive(); 1901 1902 1903 1904Any further actions for this client request are now the responsibility of `handle_send()`. 1905 1906 1907 ``''''''`` } 1908 ``''''''`` } 1909 1910 1911 1912The function `handle_send()` is invoked after the service request has been completed. 1913 1914 1915 1916 ``''''''`` void handle_send(boost::shared_ptr<std::string> /*message*/, 1917 ``''''''`` const boost::system::error_code& /*error*/, 1918 ``''''''`` std::size_t /*bytes_transferred*/) 1919 ``''''''`` { 1920 ``''''''`` } 1921 1922 ``''''''`` udp::socket socket_; 1923 ``''''''`` udp::endpoint remote_endpoint_; 1924 ``''''''`` boost::array<char, 1> recv_buffer_; 1925 ``''''''``}; 1926 1927 1928 1929See the [link boost_asio.tutorial.tutdaytime6.src full source listing] 1930 1931Return to the [link boost_asio.tutorial tutorial index] 1932 1933Previous: [link boost_asio.tutorial.tutdaytime5 Daytime.5 - A synchronous UDP daytime server] 1934 1935Next: [link boost_asio.tutorial.tutdaytime7 Daytime.7 - A combined TCP/UDP asynchronous server] 1936 1937 1938 1939[section:src Source listing for Daytime.6] 1940 1941 1942 ``''''''``// 1943 ``''''''``// server.cpp 1944 ``''''''``// ~~~~~~~~~~ 1945 ``''''''``// 1946 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 1947 ``''''''``// 1948 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 1949 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 1950 ``''''''``// 1951 1952 ``''''''``#include <ctime> 1953 ``''''''``#include <iostream> 1954 ``''''''``#include <string> 1955 ``''''''``#include <boost/array.hpp> 1956 ``''''''``#include <boost/bind/bind.hpp> 1957 ``''''''``#include <boost/shared_ptr.hpp> 1958 ``''''''``#include <boost/asio.hpp> 1959 1960 ``''''''``using boost::asio::ip::udp; 1961 1962 ``''''''``std::string make_daytime_string() 1963 ``''''''``{ 1964 ``''''''`` using namespace std; // For time_t, time and ctime; 1965 ``''''''`` time_t now = time(0); 1966 ``''''''`` return ctime(&now); 1967 ``''''''``} 1968 1969 ``''''''``class udp_server 1970 ``''''''``{ 1971 ``''''''``public: 1972 ``''''''`` udp_server(boost::asio::io_context& io_context) 1973 ``''''''`` : socket_(io_context, udp::endpoint(udp::v4(), 13)) 1974 ``''''''`` { 1975 ``''''''`` start_receive(); 1976 ``''''''`` } 1977 1978 ``''''''``private: 1979 ``''''''`` void start_receive() 1980 ``''''''`` { 1981 ``''''''`` socket_.async_receive_from( 1982 ``''''''`` boost::asio::buffer(recv_buffer_), remote_endpoint_, 1983 ``''''''`` boost::bind(&udp_server::handle_receive, this, 1984 ``''''''`` boost::asio::placeholders::error, 1985 ``''''''`` boost::asio::placeholders::bytes_transferred)); 1986 ``''''''`` } 1987 1988 ``''''''`` void handle_receive(const boost::system::error_code& error, 1989 ``''''''`` std::size_t /*bytes_transferred*/) 1990 ``''''''`` { 1991 ``''''''`` if (!error) 1992 ``''''''`` { 1993 ``''''''`` boost::shared_ptr<std::string> message( 1994 ``''''''`` new std::string(make_daytime_string())); 1995 1996 ``''''''`` socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, 1997 ``''''''`` boost::bind(&udp_server::handle_send, this, message, 1998 ``''''''`` boost::asio::placeholders::error, 1999 ``''''''`` boost::asio::placeholders::bytes_transferred)); 2000 2001 ``''''''`` start_receive(); 2002 ``''''''`` } 2003 ``''''''`` } 2004 2005 ``''''''`` void handle_send(boost::shared_ptr<std::string> /*message*/, 2006 ``''''''`` const boost::system::error_code& /*error*/, 2007 ``''''''`` std::size_t /*bytes_transferred*/) 2008 ``''''''`` { 2009 ``''''''`` } 2010 2011 ``''''''`` udp::socket socket_; 2012 ``''''''`` udp::endpoint remote_endpoint_; 2013 ``''''''`` boost::array<char, 1> recv_buffer_; 2014 ``''''''``}; 2015 2016 ``''''''``int main() 2017 ``''''''``{ 2018 ``''''''`` try 2019 ``''''''`` { 2020 ``''''''`` boost::asio::io_context io_context; 2021 ``''''''`` udp_server server(io_context); 2022 ``''''''`` io_context.run(); 2023 ``''''''`` } 2024 ``''''''`` catch (std::exception& e) 2025 ``''''''`` { 2026 ``''''''`` std::cerr << e.what() << std::endl; 2027 ``''''''`` } 2028 2029 ``''''''`` return 0; 2030 ``''''''``} 2031 2032Return to [link boost_asio.tutorial.tutdaytime6 Daytime.6 - An asynchronous UDP daytime server] 2033 2034[endsect] 2035 2036[endsect] 2037 2038[section:tutdaytime7 Daytime.7 - A combined TCP/UDP asynchronous server] 2039 2040This tutorial program shows how to combine the two asynchronous servers that we have just written, into a single server application. 2041 2042[heading The main() function] 2043 2044 2045 ``''''''``int main() 2046 ``''''''``{ 2047 ``''''''`` try 2048 ``''''''`` { 2049 ``''''''`` boost::asio::io_context io_context; 2050 2051 2052 2053We will begin by creating a server object to accept a TCP client connection. 2054 2055 2056 ``''''''`` tcp_server server1(io_context); 2057 2058 2059 2060We also need a server object to accept a UDP client request. 2061 2062 2063 ``''''''`` udp_server server2(io_context); 2064 2065 2066 2067We have created two lots of work for the 2068[link boost_asio.reference.io_context io_context] object to do. 2069 2070 2071 ``''''''`` io_context.run(); 2072 ``''''''`` } 2073 ``''''''`` catch (std::exception& e) 2074 ``''''''`` { 2075 ``''''''`` std::cerr << e.what() << std::endl; 2076 ``''''''`` } 2077 2078 ``''''''`` return 0; 2079 ``''''''``} 2080 2081 2082 2083[heading The tcp_connection and tcp_server classes] 2084 2085The following two classes are taken from [link boost_asio.tutorial.tutdaytime3 Daytime.3] . 2086 2087 2088 ``''''''``class tcp_connection 2089 ``''''''`` : public boost::enable_shared_from_this<tcp_connection> 2090 ``''''''``{ 2091 ``''''''``public: 2092 ``''''''`` typedef boost::shared_ptr<tcp_connection> pointer; 2093 2094 ``''''''`` static pointer create(boost::asio::io_context& io_context) 2095 ``''''''`` { 2096 ``''''''`` return pointer(new tcp_connection(io_context)); 2097 ``''''''`` } 2098 2099 ``''''''`` tcp::socket& socket() 2100 ``''''''`` { 2101 ``''''''`` return socket_; 2102 ``''''''`` } 2103 2104 ``''''''`` void start() 2105 ``''''''`` { 2106 ``''''''`` message_ = make_daytime_string(); 2107 2108 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_), 2109 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this())); 2110 ``''''''`` } 2111 2112 ``''''''``private: 2113 ``''''''`` tcp_connection(boost::asio::io_context& io_context) 2114 ``''''''`` : socket_(io_context) 2115 ``''''''`` { 2116 ``''''''`` } 2117 2118 ``''''''`` void handle_write() 2119 ``''''''`` { 2120 ``''''''`` } 2121 2122 ``''''''`` tcp::socket socket_; 2123 ``''''''`` std::string message_; 2124 ``''''''``}; 2125 2126 ``''''''``class tcp_server 2127 ``''''''``{ 2128 ``''''''``public: 2129 ``''''''`` tcp_server(boost::asio::io_context& io_context) 2130 ``''''''`` : io_context_(io_context), 2131 ``''''''`` acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) 2132 ``''''''`` { 2133 ``''''''`` start_accept(); 2134 ``''''''`` } 2135 2136 ``''''''``private: 2137 ``''''''`` void start_accept() 2138 ``''''''`` { 2139 ``''''''`` tcp_connection::pointer new_connection = 2140 ``''''''`` tcp_connection::create(io_context_); 2141 2142 ``''''''`` acceptor_.async_accept(new_connection->socket(), 2143 ``''''''`` boost::bind(&tcp_server::handle_accept, this, new_connection, 2144 ``''''''`` boost::asio::placeholders::error)); 2145 ``''''''`` } 2146 2147 ``''''''`` void handle_accept(tcp_connection::pointer new_connection, 2148 ``''''''`` const boost::system::error_code& error) 2149 ``''''''`` { 2150 ``''''''`` if (!error) 2151 ``''''''`` { 2152 ``''''''`` new_connection->start(); 2153 ``''''''`` } 2154 2155 ``''''''`` start_accept(); 2156 ``''''''`` } 2157 2158 ``''''''`` boost::asio::io_context& io_context_; 2159 ``''''''`` tcp::acceptor acceptor_; 2160 ``''''''``}; 2161 2162 2163 2164[heading The udp_server class] 2165 2166Similarly, this next class is taken from the [link boost_asio.tutorial.tutdaytime6 previous tutorial step] . 2167 2168 2169 ``''''''``class udp_server 2170 ``''''''``{ 2171 ``''''''``public: 2172 ``''''''`` udp_server(boost::asio::io_context& io_context) 2173 ``''''''`` : socket_(io_context, udp::endpoint(udp::v4(), 13)) 2174 ``''''''`` { 2175 ``''''''`` start_receive(); 2176 ``''''''`` } 2177 2178 ``''''''``private: 2179 ``''''''`` void start_receive() 2180 ``''''''`` { 2181 ``''''''`` socket_.async_receive_from( 2182 ``''''''`` boost::asio::buffer(recv_buffer_), remote_endpoint_, 2183 ``''''''`` boost::bind(&udp_server::handle_receive, this, 2184 ``''''''`` boost::asio::placeholders::error)); 2185 ``''''''`` } 2186 2187 ``''''''`` void handle_receive(const boost::system::error_code& error) 2188 ``''''''`` { 2189 ``''''''`` if (!error) 2190 ``''''''`` { 2191 ``''''''`` boost::shared_ptr<std::string> message( 2192 ``''''''`` new std::string(make_daytime_string())); 2193 2194 ``''''''`` socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, 2195 ``''''''`` boost::bind(&udp_server::handle_send, this, message)); 2196 2197 ``''''''`` start_receive(); 2198 ``''''''`` } 2199 ``''''''`` } 2200 2201 ``''''''`` void handle_send(boost::shared_ptr<std::string> /*message*/) 2202 ``''''''`` { 2203 ``''''''`` } 2204 2205 ``''''''`` udp::socket socket_; 2206 ``''''''`` udp::endpoint remote_endpoint_; 2207 ``''''''`` boost::array<char, 1> recv_buffer_; 2208 ``''''''``}; 2209 2210 2211 2212See the [link boost_asio.tutorial.tutdaytime7.src full source listing] 2213 2214Return to the [link boost_asio.tutorial tutorial index] 2215 2216Previous: [link boost_asio.tutorial.tutdaytime6 Daytime.6 - An asynchronous UDP daytime server] 2217 2218 2219 2220[section:src Source listing for Daytime.7] 2221 2222 2223 ``''''''``// 2224 ``''''''``// server.cpp 2225 ``''''''``// ~~~~~~~~~~ 2226 ``''''''``// 2227 ``''''''``// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 2228 ``''''''``// 2229 ``''''''``// Distributed under the Boost Software License, Version 1.0. (See accompanying 2230 ``''''''``// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 2231 ``''''''``// 2232 2233 ``''''''``#include <ctime> 2234 ``''''''``#include <iostream> 2235 ``''''''``#include <string> 2236 ``''''''``#include <boost/array.hpp> 2237 ``''''''``#include <boost/bind/bind.hpp> 2238 ``''''''``#include <boost/shared_ptr.hpp> 2239 ``''''''``#include <boost/enable_shared_from_this.hpp> 2240 ``''''''``#include <boost/asio.hpp> 2241 2242 ``''''''``using boost::asio::ip::tcp; 2243 ``''''''``using boost::asio::ip::udp; 2244 2245 ``''''''``std::string make_daytime_string() 2246 ``''''''``{ 2247 ``''''''`` using namespace std; // For time_t, time and ctime; 2248 ``''''''`` time_t now = time(0); 2249 ``''''''`` return ctime(&now); 2250 ``''''''``} 2251 2252 ``''''''``class tcp_connection 2253 ``''''''`` : public boost::enable_shared_from_this<tcp_connection> 2254 ``''''''``{ 2255 ``''''''``public: 2256 ``''''''`` typedef boost::shared_ptr<tcp_connection> pointer; 2257 2258 ``''''''`` static pointer create(boost::asio::io_context& io_context) 2259 ``''''''`` { 2260 ``''''''`` return pointer(new tcp_connection(io_context)); 2261 ``''''''`` } 2262 2263 ``''''''`` tcp::socket& socket() 2264 ``''''''`` { 2265 ``''''''`` return socket_; 2266 ``''''''`` } 2267 2268 ``''''''`` void start() 2269 ``''''''`` { 2270 ``''''''`` message_ = make_daytime_string(); 2271 2272 ``''''''`` boost::asio::async_write(socket_, boost::asio::buffer(message_), 2273 ``''''''`` boost::bind(&tcp_connection::handle_write, shared_from_this())); 2274 ``''''''`` } 2275 2276 ``''''''``private: 2277 ``''''''`` tcp_connection(boost::asio::io_context& io_context) 2278 ``''''''`` : socket_(io_context) 2279 ``''''''`` { 2280 ``''''''`` } 2281 2282 ``''''''`` void handle_write() 2283 ``''''''`` { 2284 ``''''''`` } 2285 2286 ``''''''`` tcp::socket socket_; 2287 ``''''''`` std::string message_; 2288 ``''''''``}; 2289 2290 ``''''''``class tcp_server 2291 ``''''''``{ 2292 ``''''''``public: 2293 ``''''''`` tcp_server(boost::asio::io_context& io_context) 2294 ``''''''`` : io_context_(io_context), 2295 ``''''''`` acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) 2296 ``''''''`` { 2297 ``''''''`` start_accept(); 2298 ``''''''`` } 2299 2300 ``''''''``private: 2301 ``''''''`` void start_accept() 2302 ``''''''`` { 2303 ``''''''`` tcp_connection::pointer new_connection = 2304 ``''''''`` tcp_connection::create(io_context_); 2305 2306 ``''''''`` acceptor_.async_accept(new_connection->socket(), 2307 ``''''''`` boost::bind(&tcp_server::handle_accept, this, new_connection, 2308 ``''''''`` boost::asio::placeholders::error)); 2309 ``''''''`` } 2310 2311 ``''''''`` void handle_accept(tcp_connection::pointer new_connection, 2312 ``''''''`` const boost::system::error_code& error) 2313 ``''''''`` { 2314 ``''''''`` if (!error) 2315 ``''''''`` { 2316 ``''''''`` new_connection->start(); 2317 ``''''''`` } 2318 2319 ``''''''`` start_accept(); 2320 ``''''''`` } 2321 2322 ``''''''`` boost::asio::io_context& io_context_; 2323 ``''''''`` tcp::acceptor acceptor_; 2324 ``''''''``}; 2325 2326 ``''''''``class udp_server 2327 ``''''''``{ 2328 ``''''''``public: 2329 ``''''''`` udp_server(boost::asio::io_context& io_context) 2330 ``''''''`` : socket_(io_context, udp::endpoint(udp::v4(), 13)) 2331 ``''''''`` { 2332 ``''''''`` start_receive(); 2333 ``''''''`` } 2334 2335 ``''''''``private: 2336 ``''''''`` void start_receive() 2337 ``''''''`` { 2338 ``''''''`` socket_.async_receive_from( 2339 ``''''''`` boost::asio::buffer(recv_buffer_), remote_endpoint_, 2340 ``''''''`` boost::bind(&udp_server::handle_receive, this, 2341 ``''''''`` boost::asio::placeholders::error)); 2342 ``''''''`` } 2343 2344 ``''''''`` void handle_receive(const boost::system::error_code& error) 2345 ``''''''`` { 2346 ``''''''`` if (!error) 2347 ``''''''`` { 2348 ``''''''`` boost::shared_ptr<std::string> message( 2349 ``''''''`` new std::string(make_daytime_string())); 2350 2351 ``''''''`` socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, 2352 ``''''''`` boost::bind(&udp_server::handle_send, this, message)); 2353 2354 ``''''''`` start_receive(); 2355 ``''''''`` } 2356 ``''''''`` } 2357 2358 ``''''''`` void handle_send(boost::shared_ptr<std::string> /*message*/) 2359 ``''''''`` { 2360 ``''''''`` } 2361 2362 ``''''''`` udp::socket socket_; 2363 ``''''''`` udp::endpoint remote_endpoint_; 2364 ``''''''`` boost::array<char, 1> recv_buffer_; 2365 ``''''''``}; 2366 2367 ``''''''``int main() 2368 ``''''''``{ 2369 ``''''''`` try 2370 ``''''''`` { 2371 ``''''''`` boost::asio::io_context io_context; 2372 ``''''''`` tcp_server server1(io_context); 2373 ``''''''`` udp_server server2(io_context); 2374 ``''''''`` io_context.run(); 2375 ``''''''`` } 2376 ``''''''`` catch (std::exception& e) 2377 ``''''''`` { 2378 ``''''''`` std::cerr << e.what() << std::endl; 2379 ``''''''`` } 2380 2381 ``''''''`` return 0; 2382 ``''''''``} 2383 2384Return to [link boost_asio.tutorial.tutdaytime7 Daytime.7 - A combined TCP/UDP asynchronous server] 2385 2386[endsect] 2387 2388[endsect] 2389 2390 2391[endsect] 2392