#include <assert.h> #include <signal.h> #include <unistd.h> #include <iostream> #include < string> #include <deque> #include <set> #include "boost/asio.hpp" #include "boost/thread.hpp" #include " Boost/bind.hpp "#include" boost/shared_ptr.hpp "#include" boost/enable_shared_from_this.hpp "#include" boost/thread/ Thread.hpp "#include" boost/date_time/posix_time/posix_time.hpp "#include" boost/atomic.hpp "namespace {class Echoserver;typedef boost::shared_ptr<echoserver> echoserverptr;typedef Boost::shared_ptr<boost::asio::io _service> ioserviceptr;typedef boost::shared_ptr<boost::asio::ip::tcp::socket> SocketPtr;class Connection ; typedef boost::shared_ptr<connection> CONNPTR;TYPEDEF boost::shared_ptr<std::string> StringPtr; typedef boost::shared_ptr<boost::asio::d eadline_timer> timerptr;//Guidelines 1://A socket never call ASYNC_READ/ASYNC_ Write more than 1 times, you can refer to boost doc://This operation was implemented in terms of zero or greater calls to the stream ' s asyNc_write_some function, and is known as a composed operation. The program must ensure this stream performs no other write operations (such as Async_write, the stream ' s Async_write_ Some function, or any other composed operations that perform writes) until this operation completes.//also be sure to complete the previous async operation before Launch next!! Guideline 2://operation 1 sockets, in multi-threaded conditions must be lock processing, a large lock to solve all problems, other uses are non-thread-safe.//That is, synchronous close/async_read/async_write three function calls. class Connection:public boost::enable_shared_from_this<connection> {public:enum ConnStatus {kConnected = 0, KEr Ror = 1, kclosed = 2,}; Connection (socketptr socket): Status_ (kconnected), socket_ (socket) {} ~connection () {//can be used here to reset outgoing messages in Write_queue Try logical processing Std::cout << __function__ << Std::endl; } void Start () {socket_->async_receive (Boost::asio::buffer (msgbuf_, sizeof (msgbuf_)), Boost::bind (&connectio N::readhandler, Shared_from_this (), _1, _2)); } void Close () {///duplicate call to close of socket is not a problem, but cannot call close concurrently (assuming the close interfaceExposed to the user, there is this demand). if (Status_.exchange (kclosed)! = kclosed) {//There is no problem even if you repeatedly call close of the socket, but this also guarantees that close can only be called once. Boost::lock_guard<boost::mutex> Guard (SOCKET_MUTEX_); Boost::system::error_code Errcode; if (Socket_->close (Errcode)) {Std::cerr << "close Connection Error" << Std::endl; } else {std::cerr << "Close Connection done" << Std::endl; }}} Connstatus status () {return status_.load ();} Private:void Readhandler (const boost::system::error_code& error, std::size_t bytes_transferred) {if (!error) {/ /No error occurred (contains canceled), then the next read is initiated. The function reads some data and returns it, just right for the echo logic here. If you want to read the specified length before it is finished, use Async_read. {boost::lock_guard<boost::mutex> Guard (socket_mutex_); Socket_->async_receive (Boost::asio::buffer (msgbuf_, sizeof (msgbuf_)), Boost::bind (&connection:: Readhandler, Shared_from_this (), _1, _2)); }//printf ("%.*s", (int) bytes_transferred, msgbuf_); Show here How toUse Async_write to send echo in an orderly manner under multi-threaded ASIO, and to send a message queue to send a message resend when the socket fails. Echomsg (Stringptr (New std::string (Msgbuf_, bytes_transferred))); } else if (Error = = boost::asio::error::operation_aborted) {std::cout << "Connection readhandler Canceled." &l t;< Std::endl; } else {connstatus expected = kconnected; if (Status_.compare_exchange_strong (expected, kerror)) {std::cout << "Readhandler Error." << Std::endl ; }}} void Writehandler (const boost::system::error_code& error, std::size_t bytes_transferred) {if (!error) { Boost::lock_guard<boost::mutex> Guard (SOCKET_MUTEX_); Write_queue_.pop_front (); if (Write_queue_.size ()) {Stringptr next_msg = Write_queue_.front (); Async_write guarantees that the data is all written back. Async_write (*socket_, Boost::asio::buffer (*next_msg), Boost::bind (&connection::writehandler, Shared_from_this (), _1, _2)); }} else if (Error = = Boost::asio::error::operation_aborted) {std::cout << "Connection writehandler Canceled." << Std::endl; } else {connstatus expected = kconnected; if (Status_.compare_exchange_strong (expected, kerror)) {std::cout << "Writehandler Error." << std::end L }}} void Echomsg (Stringptr msg) {boost::lock_guard<boost::mutex> guard (socket_mutex_); Write_queue_.push_back (msg); if (write_queue_.size () = = 1) {async_write (*socket_, Boost::asio::buffer (*msg), Boost::bind (&connection::writeha Ndler, Shared_from_this (), _1, _2)); }} std::d eque<stringptr> write_queue_; Boost::mutex socket_mutex_; Boost::atomic<connstatus> Status_; Char msgbuf_[1024 * 16]; Socketptr socket_;}; Class Echoserver:public boost::enable_shared_from_this<echoserver> {public:echoserver (IOServicePtr Io_ Service): Stopped_ (False), Io_service_ (Io_service), Acceptor_ (*io_service) {} ~echoserver () {//After stop, the main thread releases the reference count, etc. After Io_service processing the remaining events after the destruction, this time no longerA new connection is added,//You can close all the sockets and release the reference count. Std::cout << __function__ << Std::endl; Boost::lock_guard<boost::mutex> Guard (CONN_SET_MUTEX_); for (Connsetiter iter = Conn_set_.begin (); ITER! = Conn_set_.end (); ++iter) {(*iter)->close (); }} bool Start (const std::string& Host, unsigned short port) {Boost::system::error_code errcode; Boost::asio::ip::address address = boost::asio::ip::address::from_string (host, Errcode); if (Errcode) {return false; } if (Acceptor_.open (Boost::asio::ip::tcp::v4 (), Errcode)) {return false; } acceptor_.set_option (Boost::asio::ip::tcp::acceptor::reuse_address (true)); Boost::asio::ip::tcp::endpoint endpoint (address, port); if (Acceptor_.bind (endpoint, errcode) | | Acceptor_.listen (1024x768, Errcode)) {return false; } socketptr Socket (new Boost::asio::ip::tcp::socket (*io_service_)); Acceptor_.async_accept (*socket, Boost::bind (&echoserver::accepthandler, Shared_from_this(), socket, _1)); return true; } void Stop () {Boost::system::error_code errcode; if (Acceptor_.close (Errcode)) {Std::cerr << "close acceptor Error" << Std::endl; } Stopped_.store (True); }private:void Accepthandler (socketptr socket, const boost::system::error_code& error) {//No concurrent call if (Error = = Bo ost::asio::error::operation_aborted) {//Because acceptor is closed and cancel, no need to do anything. Std::cout << "Accept Canceled" << Std::endl; Return The user actively shuts down the server, so the operation is cancel} else if (!error) {///successful accept, creating a new connection. Std::cout << "Accept New Connection" << Std::endl; Connptr New_conn (new Connection (socket)); New_conn->start (); {boost::lock_guard<boost::mutex> Guard (conn_set_mutex_); Conn_set_.insert (New_conn); } timerptr Socket_timer (new Boost::asio::d Eadline_timer (*io_service_)); Socket_timer->expires_from_now (boost::p osix_time::seconds (1)); Socket_timeR->async_wait (Boost::bind (&echoserver::checksocketstatus, Shared_from_this (), New_conn, Socket_timer, _1)); } else {std::cout << "Accept Error" << Std::endl; } socketptr New_socket (new Boost::asio::ip::tcp::socket (*io_service_)); Acceptor_.async_accept (*new_socket, Boost::bind (&echoserver::accepthandler, Shared_from_this (), New_socket, _1 )); } void Checksocketstatus (Connptr conn, timerptr socket_timer, const boost::system::error_code& error) {//1, Echo The server has been called by stop, so stop the timer to release the reference count for Echoserver as soon as possible, and let the Echoserver destructor end the service. 2, Judge Conn->status () ==kerror to close the connection and remove from Connset. 3, Judge Conn->status () ==kclosed is removed from the connset. (In the future the user can get socketptr and call close at any time)//4, connect normally, continue to launch the next timer. Boost::lock_guard<boost::mutex> Guard (CONN_SET_MUTEX_); Connsetiter iter = conn_set_.find (conn); ASSERT (iter! = Conn_set_.end ()); if (Stopped_.load ()) {//Case 1//std::cout << ' Case 1 ' << Std::endl; } else if (CONn->status () = = Connection::kerror) {//Case 2//std::cout << ' case 2 ' << Std::endl; Conn->close (); Conn_set_.erase (conn); } else if (conn->status () = = connection::kclosed) {//Case 3//std::cout << ' case 3 ' << Std::endl; Conn_set_.erase (conn); } else {//std::cout << ' case 4 ' << Std::endl;//Case 4 Socket_timer->expires_from_now (boost::p o Six_time::seconds (1)); Socket_timer->async_wait (Boost::bind (&echoserver::checksocketstatus, Shared_from_this (), Conn, socket_ Timer, _1)); }} typedef std::set<connptr> Connset; typedef connset::iterator Connsetiter; Boost::atomic<bool> Stopped_; Boost::mutex conn_set_mutex_; Connset conn_set_; Ioserviceptr Io_service_; Boost::asio::ip::tcp::acceptor Acceptor_; Auto-close while destructor.}; Volatile sig_atomic_t g_shutdown_server = 0;void shutdownserverhandler (int signo) {g_shutdown_server = 1;} void Setupsignalhandler () {sigset_t Sigset; Sigfillset (&sigset); Sigdelset (&sigset, SIGTERM); Sigdelset (&sigset, SIGINT); Sigprocmask (Sig_setmask, &sigset, NULL); struct Sigaction Act; memset (&act, 0, sizeof (ACT)); Act.sa_handler = Shutdownserverhandler; Sigaction (SIGINT, &act, NULL); Sigaction (SIGTERM, &act, NULL);} void Asiothreadmain (Ioserviceptr io_service) {//Multithreading call this io_service run Leader-follower model// Initialize hangs a echoserver acceptor on the inside, the main thread calls stop and reset releases the reference, and//Io_service will dispose of the acceptor remaining events after releasing the reference count to make Echoserver destructor, In Echoserver, all the online sockets are close and the reference count is released, and the reference count is released after Io_service has processed all the remaining events of the socket//so that all sockets are refactored, eventually io_ The service will automatically exit the thread without any events. Io_service->run ();}} int main (int argc, char** argv) {Setupsignalhandler (); Ioserviceptr Io_service (New Boost::asio::io_service ()); Echoserverptr Echo_server (New Echoserver (Io_service)); if (!echo_server->start ("0.0.0.0", 7566)) {return-1; } Boost::thread_group asio_threads; for (int i = 0; i < ++i) {Asio_threadS.create_thread (Boost::bind (Asiothreadmain, Io_service)); } while (!g_shutdown_server) {sleep (1); } echo_server->stop (); Close listener echo_server.reset (); Release the reference count and let Echo_server destructor. Asio_threads.join_all (); Wait for ASIO natural exit std::cout << "Stopped ..." << Std::endl; return 0;}
How to use Boost::asio correctly in multithreaded Leader-follower mode.