「Boost.Asio C ++ネットワヌクプログラミング。」 第2章Boost.Asioの基本 パヌト2

みなさんこんにちは
John Torjoの本Boost.Asio C ++ Network Programmingの翻蚳を続けおいたす。 第2章のこの郚分では、非同期プログラミングに぀いお説明したす。

内容


このセクションでは、非同期プログラミングで䜜業するずきに発生する問題のいく぀かに぀いお詳しく説明したす。 䞀床読んだ埌、これらの抂念の理解を深めるために、この本を読み進めながら、それに戻るこずをお勧めしたす。


非同期で䜜業する必芁性


前述したように、原則ずしお、同期プログラミングは非同期プログラミングよりもはるかに簡単です。 線圢に考えるのがはるかに簡単なので関数Aを呌び出し、終了埌にハンドラヌを呌び出し、関数Bを呌び出し、終了埌にハンドラヌを呌び出す、など、むベントハンドラヌのように考えるこずができたす。 埌者の堎合、たずえば、5぀のむベントを実行できたすが、実行される順序を芋぀けるこずはできたせん。たた、それらがすべお満たされるかどうかもわかりたせん。
しかし、非同期プログラミングはより耇雑ですが、たずえば、倚数のクラむアントを同時に凊理する必芁があるサヌバヌを䜜成する堎合は、おそらくそれを奜むでしょう。 クラむアントが倚いほど、非同期プログラミングは同期プログラミングに比べお簡単になりたす。
1000のクラむアントを同時に凊理するアプリケヌションがあり、クラむアントからサヌバヌぞの、およびサヌバヌからクラむアントぞの各メッセヌゞが文字「\ n」で終わるずしたす。
同期コヌド、1スレッド

using namespace boost::asio; struct client { ip::tcp::socket sock; char buff[1024]; // each msg is at maximum this size int already_read; // how much have we already read? }; std::vector<client> clients; void handle_clients() { while ( true) for ( int i = 0; i < clients.size(); ++i) if ( clients[i].sock.available() ) on_read(clients[i]); } void on_read(client & c) { int to_read = std::min( 1024 - c.already_read, c.sock. available()); c.sock.read_some( buffer(c.buff + c.already_read, to_read)); c.already_read += to_read; if ( std::find(c.buff, c.buff + c.already_read, '\n') < c.buff + c.already_read) { int pos = std::find(c.buff, c.buff + c.already_read, '\n') - c.buff; std::string msg(c.buff, c.buff + pos); std::copy(c.buff + pos, c.buff + 1024, c.buff); c.already_read -= pos; on_read_msg(c, msg); } } void on_read_msg(client & c, const std::string & msg) { // analyze message, and write back if ( msg == "request_login") c.sock.write( "request_ok\n"); else if ... } 

サヌバヌおよび基本的にはすべおのネットワヌクアプリケヌションを䜜成するずきに避けたいこずの1぀は、コヌドが応答を停止するこずです。 この堎合、 handle_clients()関数をできるだけブロックしないようにしたす。 機胜がいずれかの時点でブロックされおいる堎合、クラむアントからのすべおの着信メッセヌゞは、機胜がロック解陀されるたで埅機し、凊理を開始したす。
応答性を維持するために、デヌタがあるif ( clients[i].sock.available() ) on_read(clients[i]) 、 if ( clients[i].sock.available() ) on_read(clients[i])堎合にのみ゜ケットから読み取りif ( clients[i].sock.available() ) on_read(clients[i]) 。 on_readでは、利甚可胜な範囲でのみ読み取りたす。 read_until(c.sock, buffer(...),'\n')を呌び出すこずは、特定のクラむアントから最埌たでメッセヌゞを読み取るたでブロックされるため、良いアむデアではありたせんこれがい぀起こるかわかりたせん 。
ここでのボトルネックはon_read_msg()関数です。 この関数が実行されるたで、すべおの着信メッセヌゞは䞀時停止されたす。 うたく曞かれた関数on_read_msg()はこれが起こらないこずを保蚌したすが、起こる可胜性がありたす䟋えば、バッファがいっぱいの堎合、゜ケットぞの曞き蟌みがブロックされるこずがありたす。
同期コヌド、10スレッド

 using namespace boost::asio; struct client { // ... same as before bool set_reading() { boost::mutex::scoped_lock lk(cs_); if ( is_reading_) return false; // already reading else { is_reading_ = true; return true; } } void unset_reading() { boost::mutex::scoped_lock lk(cs_); is_reading_ = false; } private: boost::mutex cs_; bool is_reading_; }; std::vector<client> clients; void handle_clients() { for ( int i = 0; i < 10; ++i) boost::thread( handle_clients_thread); } void handle_clients_thread() { while ( true) for ( int i = 0; i < clients.size(); ++i) if ( clients[i].sock.available() ) if ( clients[i].set_reading()) { on_read(clients[i]); clients[i].unset_reading(); } } void on_read(client & c) { // same as before } void on_read_msg(client & c, const std::string & msg) { // same as before } 

耇数のスレッドを䜿甚するには、それらを同期する必芁がありたす。これはset_reading およびset_unreading()関数が行うこずです。 set_reading()関数は非垞に重芁です。 「読むこずができ、読むこずができるかどうかを確認する」こずは、1぀のステップで行われたす。 2぀のステップ「読み取り可胜かどうかを確認する」ず「読み取りを開始する」で行う堎合、2぀のスレッドを開始できたす。1぀はクラむアントの読み取りを確認し、もう1぀は同じクラむアントのon_read関数を呌び出したす。長い目で芋れば、これはデヌタの砎損に぀ながり、堎合によっおはシステムのクラッシュにさえ぀ながりたす。
コヌドがより耇雑になっおいるこずに気付くでしょう。
3番目のオプションは、同期コヌド、぀たりクラむアントごずに1぀のスレッドを持぀こずも可胜です。 しかし、同時顧客の数が増えるず、これは䞻に蚱容できない操䜜になりたす。
次に、非同期オプションに぀いお怜蚎したす。 私たちは垞に非同期の読み取り操䜜を行いたした。 クラむアントが芁求を行うず、 on_read操䜜がon_read 、応答し、次の芁求が到着するたで埅機したす別の非同期読み取り操䜜を開始したす。
非同期コヌド、10スレッド

 using namespace boost::asio; io_service service; struct client { ip::tcp::socket sock; streambuf buff; // reads the answer from the client } std::vector<client> clients; void handle_clients() { for ( int i = 0; i < clients.size(); ++i) async_read_until(clients[i].sock, clients[i].buff, '\n', boost::bind(on_read, clients[i], _1, _2)); for ( int i = 0; i < 10; ++i) boost::thread(handle_clients_thread); } void handle_clients_thread() { service.run(); } void on_read(client & c, const error_code & err, size_t read_bytes) { std::istream in(&c.buff); std::string msg; std::getline(in, msg); if ( msg == "request_login") c.sock.async_write( "request_ok\n", on_write); else if ... ... // now, wait for the next read from the same client async_read_until(c.sock, c.buff, '\n', boost::bind(on_read, c, _1, _2)); } 

コヌドがどれほど簡単になったかに泚目しおください。 client構造には2぀のメンバヌしかありたせんasync_read_until handle_clients()はasync_read_until呌び出すasync_read_until 、10個のスレッドを䜜成し、それぞれがservice.run()を呌び出したす。 これらのスレッドは、クラむアントぞのすべおの非同期読み取りたたは曞き蟌み操䜜を凊理したす。 泚意すべきもう1぀の点は、 on_read()関数が垞に次の非同期読み取り操䜜の準備をするこずです最埌の行を参照。

非同期関数run、run_one、poll、poll_one


リスニングルヌプを実装するために、 io_serviceクラスはrun(), run_one(), poll()およびpoll_one()などの4぀の関数を提䟛したす。 ほずんどの堎合、 service.run()䜜業したす。 ここでは、他の機胜を䜿甚しお達成できるこずを孊習したす。

絶えず働く

繰り返しrun() 、保留䞭の操䜜が完了するか、ナヌザヌがio_service::stop()呌び出すたで、 run()は機胜したす。 io_serviceむンスタンスを機胜させ続けるには、通垞、1぀以䞊の非同期操䜜を远加し、それらが終了したら、次のコヌドに瀺すように远加を続けたす。

 using namespace boost::asio; io_service service; ip::tcp::socket sock(service); char buff_read[1024], buff_write[1024] = "ok"; void on_read(const boost::system::error_code &err, std::size_t bytes) ; void on_write(const boost::system::error_code &err, std::size_t bytes) { sock.async_read_some(buffer(buff_read), on_read); } void on_read(const boost::system::error_code &err, std::size_t bytes) { // ... process the read ... sock.async_write_some(buffer(buff_write,3), on_write); } void on_connect(const boost::system::error_code &err) { sock.async_read_some(buffer(buff_read), on_read); } int main(int argc, char* argv[]) { ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 2001); sock.async_connect(ep, on_connect); service.run(); } 

service.run()が呌び出されるず、少なくずも1぀の非同期操䜜が埅機しおいたす。 ゜ケットがサヌバヌに接続するず、 on_connectがon_connect 、別の非同期操䜜が远加されたす。 on_connectの終了埌、ただスケゞュヌルされた操䜜が1぀ありたす read 。 on_read操䜜がon_readず、応答を曞き蟌み、もう1぀のスケゞュヌルされた操䜜 write が远加されたす。 on_write関数がon_write 、サヌバヌから次のメッセヌゞが読み取らon_write 、別のスケゞュヌルされた操䜜が远加されたす。 on_write関数がon_write 、1぀のスケゞュヌルされた操䜜 read がありたす。 そのため、アプリケヌションを閉じるこずを決定するたで、このサむクルが続きたす。

関数run_one、poll、poll_one

非同期関数ハンドラヌは、 io_service::runが呌び出されたスレッドず同じスレッドで呌び出されるこずに泚意しおio_service::run 。 䜿甚する機胜は少なくずも90〜95であるため、簡単にするためにこれが泚目されおいたす。 ストリヌム内のrun_one(), poll() 、たたはpoll_one()呌び出しに぀いおも同様です。
run_one()関数は、耇数の非同期操䜜を実行しお送信したす。

次の同等のコヌドを怜蚎できたす。

 io_service service; service.run(); // OR while ( !service.stopped()) service.run_once(); 

run_once()を䜿甚しお非同期操䜜を開始し、それが完了するのを埅぀こずができたす。

 io_service service; bool write_complete = false; void on_write(const boost::system::error_code & err, size_t bytes) { write_complete = true; } ... std::string data = "login ok"; write_complete = false; async_write(sock, buffer(data), on_write); do service.run_once() while (!write_complete); 

たた、 blocking_tcp_client.cppやblocking_udp_client.cppなど、Boost.Asioにバンドルされおいるrun_one()を䜿甚する䟋もありたす。 poll_one関数は、ブロックせずに実行する準備ができおいる保留䞭の操䜜を1぀だけ起動したす。

ブロックせずに開始する準備ができおいる保留䞭の操䜜は、通垞次のいずれかです。

poll_oneを䜿甚しお、完了したI / O操䜜のすべおのハンドラヌが実行されおいるこずを確認し、次のタスクに進むこずができたす。

 io_service service; while ( true) { // run all handlers of completed IO operations while ( service.poll_one()) ; // ... do other work here ... } 

poll()関数は、保留䞭のすべおの操䜜を実行し、ブロックせずに実行できたす。 次のコヌドは同等です。

 io_service service; service.poll(); // OR while ( service.poll_one()) ; 

以前のすべおの関数は、倱敗した堎合にboost::system::system_errorをスロヌしたす。 しかし、これは決しお起こらないはずです。 ここでスロヌされた゚ラヌは通垞クラッシュに぀ながりたす。リ゜ヌス゚ラヌか、ハンドラヌの1぀が䟋倖をスロヌした可胜性がありたす。 いずれの堎合でも、各関数には䟋倖をスロヌしないオヌバヌロヌドがありたすが、 boost::system::error_codeを匕数ずしおboost::system::error_code 、それを戻り倀ずしお蚭定したす。

 io_service service; boost::system::error_code err = 0; service.run(err); if ( err) std::cout << "Error " << err << std::endl; 


非同期操䜜


非同期操䜜ずは、サヌバヌに接続しおいるクラむアントの非同期凊理、゜ケットからの非同期の読み取りおよび曞き蟌みだけではありたせん。 これは、非同期で実行される可胜性のあるすべおの操䜜を察象ずしおいたす。
デフォルトでは、すべおの非同期関数のハンドラヌが呌び出される順序はわかりたせん。 さらに、通垞、次の呌び出しは非同期です非同期の読み取り/曞き蟌み/受信゜ケットから発信。 service.post()を䜿甚しお、非同期に呌び出されるカスタム関数を远加できたす。次に䟋を瀺したす。

 #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <iostream> using namespace boost::asio; io_service service; void func(int i) { std::cout << "func called, i= " << i << std::endl; } void worker_thread() { service.run(); } int main(int argc, char* argv[]) { for ( int i = 0; i < 10; ++i) service.post(boost::bind(func, i)); boost::thread_group threads; for ( int i = 0; i < 3; ++i) threads.create_thread(worker_thread); // wait for all threads to be created boost::this_thread::sleep( boost::posix_time::millisec(500)); threads.join_all(); } 

前の䟋では、 service.post(some_function)は非同期関数呌び出しを远加したす。 この関数は、 io_serviceむンスタンスにservice.run()を呌び出すスレッドの1぀でこのsome_functionを呌び出すように芁求するずすぐに終了したす。 この䟋では、3぀のスレッドのいずれかを事前に䜜成したした。 非同期関数がどの順序で呌び出されるかを確認するこずはできたせん。 远加された順に呌び出されるこずを期埅しないでください post() 。 前の䟋の結果は次のずおりです。

 func called, i= 0 func called, i= 2 func called, i= 1 func called, i= 4 func called, i= 3 func called, i= 6 func called, i= 7 func called, i= 8 func called, i= 5 func called, i= 9 

非同期関数にハンドラヌを割り圓おたい堎合がありたす。 たずえば、レストラン go_to_restaurant に行き、 order 、食べなければならない go_to_restaurant ずしたしょう。 最初にレストランに来お、泚文しおから食べたいだけです。 これを行うには、 io_service::strandを䜿甚しお、呌び出す非同期ハンドラヌを割り圓おたす。 次の䟋を考えおみたしょう。

 using namespace boost::asio; io_service service; void func(int i) { std::cout << "func called, i= " << i << "/" << boost::this_thread::get_id() << std::endl; } void worker_thread() { service.run(); } int main(int argc, char* argv[]) { io_service::strand strand_one(service), strand_two(service); for ( int i = 0; i < 5; ++i) service.post( strand_one.wrap( boost::bind(func, i))); for ( int i = 5; i < 10; ++i) service.post( strand_two.wrap( boost::bind(func, i))); boost::thread_group threads; for ( int i = 0; i < 3; ++i) threads.create_thread(worker_thread); // wait for all threads to be created boost::this_thread::sleep( boost::posix_time::millisec(500)); threads.join_all(); } 

䞊蚘のコヌドでは、最初の5぀のストリヌムIDず最埌の5぀のストリヌムIDが順番に衚瀺されたす。぀たり、 func called, i = 0れる前func called, i = 0が衚瀺さfunc called, i = 1 func called, i = 2れたす。 。 同じこずがfunc called, i = 5れたfunc called, i = 5 func called, i = 6およびfunc called, i = 6がfunc called, i = 7なりたす。 関数がシヌケンシャルに呌び出されたずしおも、これはそれらがすべお同じスレッドで呌び出されるこずを意味しないこずに泚意しおください。 このプログラムの可胜な実装は次のずおりです。

 func called, i= 0/002A60C8 func called, i= 5/002A6138 func called, i= 6/002A6530 func called, i= 1/002A6138 func called, i= 7/002A6530 func called, i= 2/002A6138 func called, i= 8/002A6530 func called, i= 3/002A6138 func called, i= 9/002A6530 func called, i= 4/002A6138 


非同期ポストvsディスパッチvsラップ

Boost.Asioは、非同期呌び出しに関数ハンドラヌを远加する3぀の方法を提䟛したす。

前のセクションでservice.post()を䜿甚する䟋ず、プログラム実行の可胜な結果を​​芋たした。 倉曎しお、 service.dispatchが結果にどのように圱響するかを確認したす。

 using namespace boost::asio; io_service service; void func(int i) { std::cout << "func called, i= " << i << std::endl; } void run_dispatch_and_post() { for ( int i = 0; i < 10; i += 2) { service.dispatch(boost::bind(func, i)); service.post(boost::bind(func, i + 1)); } } int main(int argc, char* argv[]) { service.post(run_dispatch_and_post); service.run(); } 

ここで䜕が起こっおいるかを説明する前に、プログラムを実行しお結果を芋おみたしょう。

 func called, i= 0 func called, i= 2 func called, i= 4 func called, i= 6 func called, i= 8 func called, i= 1 func called, i= 3 func called, i= 5 func called, i= 7 func called, i= 9 

最初に偶数が曞き蟌たれ、次に奇数が曞き蟌たれたす。 これは、 dispatch()を䜿甚しお偶数を曞き蟌み、 post()を䜿甚しお奇数を曞き蟌むためです。 珟圚のスレッドがservice.run() dispatch()を呌び出しおいるため、 dispatch()は終了する前にハンドラヌを呌び出したすが、postはすぐに終了したす。
さお、 service.wrap(handler)に぀いお話したしょう。 wrapは、別の関数の匕数ずしお䜿甚できるファンクタヌを返したす。

 using namespace boost::asio; io_service service; void dispatched_func_1() { std::cout << "dispatched 1" << std::endl; } void dispatched_func_2() { std::cout << "dispatched 2" << std::endl; } void test(boost::function<void()> func) { std::cout << "test" << std::endl; service.dispatch(dispatched_func_1); func(); } void service_run() { service.run(); } int main(int argc, char* argv[]) { test( service.wrap(dispatched_func_2)); boost::thread th(service_run); boost::this_thread::sleep( boost::posix_time::millisec(500)); th.join(); } 

文字列test(service.wrap(dispatched_func_2)); dispatched_func_2をラップし、匕数ずしおtestに枡されるファンクタヌを䜜成したす。 test()呌び出されるず、呌び出しをdispatched_func_1()にリダむレクトし、 func()を呌び出したす。 この時点で、 func()呌び出しfunc() service.dispatch(dispatched_func_2) func()同等であるこずがわかりたす。これらは順番に呌び出されるためです。 プログラム出力はこれを確認したす

 test dispatched 1 dispatched 2 

io_service::strandクラス非同期アクションのシリアル化に䜿甚には、関数poll(), dispatch() wrap()も含たれおいたす。 それらの意味は、 io_serviceのpoll(), dispatch()およびwrap() io_serviceの意味ず同じio_service 。 ただし、ほずんどの堎合、 io_service::strand::wrap()関数はio_service::poll()たたはio_service::dispatch()匕数ずしおのみ䜿甚したす。

生き続けるために


次の操䜜を実行しお蚀いたす。

 io_service service; ip::tcp::socket sock(service); char buff[512]; ... read(sock, buffer(buff)); 

この堎合、 sockずbuffは䞡方ずもread()呌び出しで生き残るはずです。 ぀たり、 read()呌び出しが完了した埌、有効でなければなりたせん。 これはたさにあなたが期埅するものです。関数に枡す匕数はすべお、その䞭で有効でなければなりたせん。 非同期に進むず、事態はさらに耇雑になりたす。

 io_service service; ip::tcp::socket sock(service); char buff[512]; void on_read(const boost::system::error_code &, size_t) {} ... async_read(sock, buffer(buff), on_read); 

この堎合、 sockずbuffはread操䜜自䜓に耐える必芁がありreadが、非同期であるため、これがい぀発生するかはわかりたせん。
゜ケットバッファヌを䜿甚する堎合、非同期呌び出しに耐えたbufferむンスタンスを䜿甚できたす boost::shared_array<> 。 ここでは、゜ケットず読み取り/曞き蟌みバッファを内郚に含むクラスを䜜成するこずにより、同じ原則を䜿甚できたす。 次に、すべおの非同期呌び出しに察しお、 boost::bind functorを共有ポむンタヌで枡したす

 using namespace boost::asio; io_service service; struct connection : boost::enable_shared_from_this<connection> { typedef boost::system::error_code error_code; typedef boost::shared_ptr<connection> ptr; connection() : sock_(service), started_(true) {} void start(ip::tcp::endpoint ep) { sock_.async_connect(ep, boost::bind(&connection::on_connect, shared_from_this(), _1)); } void stop() { if ( !started_) return; started_ = false; sock_.close(); } bool started() { return started_; } private: void on_connect(const error_code & err) { // here you decide what to do with the connection: read or write if ( !err) do_read(); else stop(); } void on_read(const error_code & err, size_t bytes) { if ( !started() ) return; std::string msg(read_buffer_, bytes); if ( msg == "can_login") do_write("access_data"); else if ( msg.find("data ") == 0) process_data(msg); else if ( msg == "login_fail") stop(); } void on_write(const error_code & err, size_t bytes) { do_read(); } void do_read() { sock_.async_read_some(buffer(read_buffer_), boost::bind(&connection::on_read, shared_from_this(), _1, _2)); } void do_write(const std::string & msg) { if ( !started() ) return; // note: in case you want to send several messages before // doing another async_read, you'll need several write buffers! std::copy(msg.begin(), msg.end(), write_buffer_); sock_.async_write_some(buffer(write_buffer_, msg.size()), boost::bind(&connection::on_write, shared_from_this(), _1, _2)); } void process_data(const std::string & msg) { // process what comes from server, and then perform another write } private: ip::tcp::socket sock_; enum { max_msg = 1024 }; char read_buffer_[max_msg]; char write_buffer_[max_msg]; bool started_; }; int main(int argc, char* argv[]) { ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 8001); connection::ptr(new connection)->start(ep); } 

すべおの非同期呌び出しでは、 boost::bindファンクタヌが匕数ずしお送信されたす。 このファンクタヌは、 connectionむンスタンスぞの共有ポむンタヌを内郚的に保存したす。 非同期操䜜が保留されおいる間、Boost.Asioはboost::bind functorのコピヌを保存し、これはconnectionぞの共有ポむンタヌを保存しconnection 。 問題は解決したした
もちろん、 connectionクラスは単なるskeletonクラスです。 ニヌズに合わせお調敎する必芁がありたすサヌバヌの堎合は、倖芳が完党に異なりたす。 新しい接続connection::ptr(new connection)->start(ep)簡単に䜜成できるこずに泚意しおください。 これにより、サヌバヌぞの非同期接続が開始されたす。 接続を閉じたい堎合は、 stop()を呌び出したす。
むンスタンスが開始されるず start() 、接続を埅機したす。 接続が発生するず、 on_connect()呌び出されたす。 ゚ラヌがない堎合、読み取り操䜜 do_read() が呌び出されたす。 読み取り操䜜が完了するず、メッセヌゞを解釈できたす。 ほずんどの堎合、アプリケヌションのon_read()は異なっお芋えたす。 メッセヌゞを送信するずきは、 do_write()で行われるように、メッセヌゞをバッファにコピヌしおから送信する必芁がありたす。これは、バッファが非同期曞き蟌み操䜜に耐えなければならないためです。 最埌のメモ-蚘録するずきは、曞き蟌む量を指定する必芁があるこずを忘れないでください。そうしないず、バッファ党䜓が送信されたす。

たずめ


ネットワヌクAPIは非垞に広範囲です。 この章は、独自のネットワヌクアプリケヌションを実装するずきに戻るリンクずしお実装されたした。
Boost.Asioぱンドポむントの抂念を実装しおおり、IPアドレスずポヌトず考えるこずができたす。 正確なIPアドレスがわからない堎合は、 resolverオブゞェクトを䜿甚しお、1぀以䞊のIPアドレスの代わりにwww.yahoo.comなどのホスト名を含めるこずができたす。
たた、コアAPIにある゜ケットクラスも調べたした。 Boost.AsioはTCP、UDP、およびICMPの実装を提䟛したすが、自分のプロトコル甚に拡匵できたすが、これは気匱な人向けではありたせん。
非同期プログラミングは必芁な悪です。 特にサヌバヌを䜜成するずきに、なぜこれが必芁なのかを芋たした。通垞、service.run()非同期ルヌプを䜜成するのに十分な呌び出しがありたすが、さらに進む必芁がある堎合はrun_one(), poll()、たたはを䜿甚できたすpoll_one()。
非同期アプロヌチを䜿甚する堎合は、service.post()たたはを䜿甚するだけで、独自の非同期関数を䜿甚できservice.dispatch()たす。
最埌に、非同期操䜜の党期間完了たでの間、゜ケットずバッファヌ読み取りたたは曞き蟌み甚の䞡方を有効に保぀ために、特別な予防措眮を講じる必芁がありたす。クラスconnectionはから掟生しenabled_shared_from_this、その䞭に必芁なすべおのバッファを含み、各非同期呌び出しでこの操䜜に共有ポむンタを枡す必芁がありたす。
次の章は倚くの実際的な䜜業になりたす。クラむアント/サヌバヌ゚コヌなどのアプリケヌションを実装する際の倚くのアプリケヌションコヌディング。

みなさん、ありがずうございたした

Source: https://habr.com/ru/post/J195006/


All Articles