Boost::Asioでは、非同期処理を推奨しており、必要なときにイベントを発生させて処理させることで、単体スレッドでもパケットのやり取りができるようになるようです。
Boost::AsioではProactorデザインパターンを使っているようです。
http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html
どのようなデザインパターンかは勉強不足のため分りません(汗)が、キューに(イベント)ハンドラーを入れていき、イベントが完了したら完了(イベント)ハンドラーを呼ぶらしいです。
ソースコードは以下です。
ServerModule.hpp
#pragma once
#ifndef __ASIO_SERVER_MODULE__
#define __ASIO_SERVER_MODULE__
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
namespace thorny_road{
using namespace boost::asio ;
class ServerModule : public boost::noncopyable
{
private:
io_service& io ;
ip::tcp::acceptor accept ;
boost::asio::streambuf buf ;
ip::tcp::socket socket ;
public:
ServerModule(io_service& io, const short port)
:io(io),accept(io,ip::tcp::endpoint(ip::tcp::v4(),port)),socket(io)
{
start_accept() ;
}
void start_accept()
{
// Enqueue Accept Handler
accept.async_accept(
socket,
boost::bind(&ServerModule::accept_ok,this,_1) ) ;
}
void accept_ok(const boost::system::error_code e)
{
// Enqueue Read Handler
async_read_until(
socket,
buf,
'\n',
boost::bind(&ServerModule::read_ok,this,_1) ) ;
}
void read_ok(const boost::system::error_code e)
{
std::iostream ios(&buf) ;
std::string tmp ;
ios >> tmp ; // get input stream
ios << tmp <<std::endl ; // retrun as it is
// Enqueue Read Handler
async_write(
socket,
buf,
boost::bind(&ServerModule::write_ok,this,_1) ) ;
}
void write_ok(const boost::system::error_code e)
{
// Finish
}
} ;
} // namespace snb
#endif //__ASIO_SERVER_MODULE__
Main.cpp
#include "stdafx.h"
#include "ServerModule.hpp"
#include <boost/asio.hpp>
using namespace thorny_road ;
int _tmain(int argc, _TCHAR* argv[])
{
boost::asio::io_service io ;
ServerModule svr(io,2085) ; // Start Server Module
io.run() ;
return 0;
}
非同期処理ではSocketを使って相互処理を行います。同期処理ではaccept.accept(...)を使いましたが、非同期処理ではaccept.async_accept(...)を使います。第一引数がSocketで、第二引数が完了ハンドラーとなります。ですので、この場合、接続が完了するとaccept_ok(...)が呼ばれます。boost::bind(...)は関数オブジェクトを簡単に使うためにBoostで用意されているものです。DELPHIの「.... of object」みたいなものかな?
async_read_until(...)は指定した完了条件になるまで受信する非同期処理関数です。この場合、第三引数に改行コードを設定しているので、改行を受信と完了ハンドラーが呼ばれます。
async_write(...)は指定したストリームに入っている情報を送信します。
一般にBoost::Asioではboost::asio::streambufを使ってバッファ処理をするのが良いようです。勉強不足(汗)で利点はよく分りません・・・。
メイン処理(_tmain)ではio.run()を追加しています。これは非同期処理ではハンドラーをキューに入れると、待機せずに処理が戻ってくるためです。io.run()で「ハンドラーが無くなる」までループします。
前回同様telnetから接続すると、入力したものがそのままエコーバックされると思います。
現状のままだと、一度やり取りをするとプログラムが終わってしまいます。そこで、write_okの中でstart_accept()を呼ぶようにします。こうすることで再び接続待ちになります。
void write_ok(const boost::system::error_code e)
{
// Finish
start_accept() ;
}
ただし、この場合、io.run()が無限にループするようになります。
次はパケットのやり取り部分を分けたいと思います。
ソースコードは自由に使用ください。ただし、問題が起きても責任はとりません。
0 件のコメント:
コメントを投稿