process_per_connection.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. //
  2. // process_per_connection.cpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #include <boost/asio/io_context.hpp>
  11. #include <boost/asio/ip/tcp.hpp>
  12. #include <boost/asio/signal_set.hpp>
  13. #include <boost/asio/write.hpp>
  14. #include <cstdlib>
  15. #include <iostream>
  16. #include <sys/types.h>
  17. #include <sys/wait.h>
  18. #include <unistd.h>
  19. using boost::asio::ip::tcp;
  20. class server
  21. {
  22. public:
  23. server(boost::asio::io_context& io_context, unsigned short port)
  24. : io_context_(io_context),
  25. signal_(io_context, SIGCHLD),
  26. acceptor_(io_context, {tcp::v4(), port}),
  27. socket_(io_context)
  28. {
  29. wait_for_signal();
  30. accept();
  31. }
  32. private:
  33. void wait_for_signal()
  34. {
  35. signal_.async_wait(
  36. [this](boost::system::error_code /*ec*/, int /*signo*/)
  37. {
  38. // Only the parent process should check for this signal. We can
  39. // determine whether we are in the parent by checking if the acceptor
  40. // is still open.
  41. if (acceptor_.is_open())
  42. {
  43. // Reap completed child processes so that we don't end up with
  44. // zombies.
  45. int status = 0;
  46. while (waitpid(-1, &status, WNOHANG) > 0) {}
  47. wait_for_signal();
  48. }
  49. });
  50. }
  51. void accept()
  52. {
  53. acceptor_.async_accept(
  54. [this](boost::system::error_code ec, tcp::socket new_socket)
  55. {
  56. if (!ec)
  57. {
  58. // Take ownership of the newly accepted socket.
  59. socket_ = std::move(new_socket);
  60. // Inform the io_context that we are about to fork. The io_context
  61. // cleans up any internal resources, such as threads, that may
  62. // interfere with forking.
  63. io_context_.notify_fork(boost::asio::io_context::fork_prepare);
  64. if (fork() == 0)
  65. {
  66. // Inform the io_context that the fork is finished and that this
  67. // is the child process. The io_context uses this opportunity to
  68. // create any internal file descriptors that must be private to
  69. // the new process.
  70. io_context_.notify_fork(boost::asio::io_context::fork_child);
  71. // The child won't be accepting new connections, so we can close
  72. // the acceptor. It remains open in the parent.
  73. acceptor_.close();
  74. // The child process is not interested in processing the SIGCHLD
  75. // signal.
  76. signal_.cancel();
  77. read();
  78. }
  79. else
  80. {
  81. // Inform the io_context that the fork is finished (or failed)
  82. // and that this is the parent process. The io_context uses this
  83. // opportunity to recreate any internal resources that were
  84. // cleaned up during preparation for the fork.
  85. io_context_.notify_fork(boost::asio::io_context::fork_parent);
  86. // The parent process can now close the newly accepted socket. It
  87. // remains open in the child.
  88. socket_.close();
  89. accept();
  90. }
  91. }
  92. else
  93. {
  94. std::cerr << "Accept error: " << ec.message() << std::endl;
  95. accept();
  96. }
  97. });
  98. }
  99. void read()
  100. {
  101. socket_.async_read_some(boost::asio::buffer(data_),
  102. [this](boost::system::error_code ec, std::size_t length)
  103. {
  104. if (!ec)
  105. write(length);
  106. });
  107. }
  108. void write(std::size_t length)
  109. {
  110. boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
  111. [this](boost::system::error_code ec, std::size_t /*length*/)
  112. {
  113. if (!ec)
  114. read();
  115. });
  116. }
  117. boost::asio::io_context& io_context_;
  118. boost::asio::signal_set signal_;
  119. tcp::acceptor acceptor_;
  120. tcp::socket socket_;
  121. std::array<char, 1024> data_;
  122. };
  123. int main(int argc, char* argv[])
  124. {
  125. try
  126. {
  127. if (argc != 2)
  128. {
  129. std::cerr << "Usage: process_per_connection <port>\n";
  130. return 1;
  131. }
  132. boost::asio::io_context io_context;
  133. using namespace std; // For atoi.
  134. server s(io_context, atoi(argv[1]));
  135. io_context.run();
  136. }
  137. catch (std::exception& e)
  138. {
  139. std::cerr << "Exception: " << e.what() << std::endl;
  140. }
  141. }