异步服务端

这个图表是相当复杂的;从Boost.Asio出来你能够看到4个箭头指向on_accept。on_read,on_write和on_check_ping。

着也就意味着你永远不知道哪个异步调用是下一个完毕的调用。可是你能够确定的是它是这4个操作中的一个。

如今。我们是异步的了;我们能够继续保持单线程。接受client连接是最简单的部分。例如以下所看到的:
ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),
8001));
void handle_accept(talk_to_client::ptr client, const error_code & err)
{
       client->start();
talk_to_client::ptr new_client = talk_to_client::new_();
acceptor.async_accept(new_client->sock(),
                             boost::bind(handle_accept,new_client,_1));
   }
int main(int argc, char* argv[]) {
       talk_to_client::ptr client = talk_to_client::new_();
acceptor.async_accept(client->sock(),boost::bind(handle_accept,client,_1));
    service.run();
}
上述代码会一直异步地等待一个新的client连接(每一个新的client连接会触发另外一个异步等待操作)。

我们须要监控client list changed事件(一个新client连接或者一个client断开连接),然后当事件发生时通知全部的client。因此,我们须要保存一个client连接的数组,否则。除非你不须要在某一时刻知道全部连接的client,你才不须要这样一个数组。
class talk_to_client; typedef boost::shared_ptr<talk_to_client>
client_ptr;
typedef std::vector<client_ptr> array;
array clients;
connection类的框架例如以下:

class talk_to_client : public boost::enable_shared_from_this<talk_to_

client>

                        , boost::noncopyable {
talk_to_client() { ... }
   public:
typedef boost::system::error_code error_code;
typedef boost::shared_ptr<talk_to_client> ptr;
void start() {
           started_ = true;
clients.push_back( shared_from_this());
last_ping = boost::posix_time::microsec_clock::local_time();
do_read(); //首先,我们等待客户端连接
       }
static ptr new_() { ptr new_(new talk_to_client); return new_; }
void stop() {
           if ( !started_) return;
started_ = false;
sock_.close();
ptr self = shared_from_this();
array::iterator it = std::find(clients.begin(), clients.end(),
   self);
clients.erase(it);
           update_clients_changed();
}
       bool started() const { return started_; }
ip::tcp::socket & sock() { return sock_;}
std::string username() const { return username_; }
void set_clients_changed() { clients_changed_ = true; }
… 
private:
       ip::tcp::socket sock_;
enum { max_msg = 1024 };
char read_buffer_[max_msg];
char write_buffer_[max_msg];
bool started_;
std::string username_;
deadline_timer timer_;
boost::posix_time::ptime last_ping;
bool clients_changed_;

};

我会用talk_to_client或者talk_to_server来调用connection类,从而使你更明确我所说的内容。
如今你须要用到之前的代码了;它和我们在client应用中所用到的是一样的。可是我们还有另外一个stop()方法,这种方法用来从client数组中移除一个client连接。

服务端持续不断地等待异步的read操作:
void on_read(const error_code & err, size_t bytes) {
if ( err) stop();
if ( !started() ) return;
std::string msg(read_buffer_, bytes);
       if ( msg.find("login ") == 0) on_login(msg);
else if ( msg.find("ping") == 0) on_ping();
else if ( msg.find("ask_clients") == 0) on_clients();
   }
void on_login(const std::string & msg) {
       std::istringstream in(msg);
in >> username_ >> username_;
do_write("login ok\n");
update_clients_changed();
   }
void on_ping() {
       do_write(clients_changed_ ? "ping client_list_changed\n" : "ping
ok\n");
       clients_changed_ = false;
}
void on_clients() {
    std::string msg;
for(array::const_iterator b =clients.begin(),e =clients.end(); b
!= e; ++b)
           msg += (*b)->username() + " ";
do_write("clients " + msg + "\n");

}

这段代码是简单易懂的;须要注意的一点是:当一个新客户端登录,我们调用update_clients_changed(),这种方法为全部客户端将clients_changed_标志为true。

服务端每收到一个请求就用正确的方式进行回复。例如以下所看到的:
void do_ping() { do_write("ping\n"); }
void do_ask_clients() { do_write("ask_clients\n"); }
void on_write(const error_code & err, size_t bytes) { do_read(); }
void do_read() {
       async_read(sock_, buffer(read_buffer_),
MEM_FN2(read_complete,_1,_2), MEM_FN2(on_read,_1,_2));
       post_check_ping();
}
   void do_write(const std::string & msg) {
if ( !started() ) return;
std::copy(msg.begin(), msg.end(), write_buffer_);
sock_.async_write_some( buffer(write_buffer_, msg.size()),
                               MEM_FN2(on_write,_1,_2));
   }
size_t read_complete(const boost::system::error_code & err, size_t
bytes) {
       // ... as before
}
在每一个write操作的末尾,on_write()方法被调用。这种方法会触发另外一个异步读操作,这种话“等待请求-回复请求”这个循环酒会一直运行,直到client断开连接或者超时。
在每次读操作開始之前,我们异步等待5秒钟来观察client是否超时。假设超时。我们关闭它的连接:
void on_check_ping() {
ptime now = microsec_clock::local_time();
if ( (now - last_ping).total_milliseconds() > 5000)
           stop();
last_ping = boost::posix_time::microsec_clock::local_time();
   }
void post_check_ping() {
       timer_.expires_from_now(boost::posix_time::millisec(5000));
       timer_.async_wait( MEM_FN(on_check_ping));
}
这就是整个服务端的实现。你能够执行并让它工作起来!
在代码中,我向你们展示了这一章我们学到的东西,为了更easy理解,我把代码略微精简了下。比方,大部分的控制台输出我都没有展示。虽然在这本书附赠的代码中它们是存在的。

我建议你自己执行这些样例,由于从头到尾读一次代码能加强你对本章展示应用的理解。

总结
我们已经学到了怎么写一些基础的client/服务端应用。

我们已经避免了一些诸如内存泄漏和死锁的低级错误。全部的编码都是框架式的。这样你就能够依据你自己的需求对它们进行扩展。

在接下来的章节中,我们会更加深入地了解使用Boost.Asio进行同步编程和异步编程的不同点。同一时候你会也学会怎样嵌入你自己的异步操作。






Boost.Asio c++ 网络编程翻译(20)的更多相关文章

  1. Boost.Asio c++ 网络编程翻译(14)

    保持活动 假如,你须要做以下的操作: io_service service; ip::tcp::socket sock(service); char buff[512]; ... read(sock, ...

  2. Boost.Asio c++ 网络编程翻译(26)

    Boost.Asio-其他特性 这章我们讲了解一些Boost.Asio不那么为人所知的特性.标准的stream和streambuf对象有时候会更难用一些,但正如你所见.它们也有它们的益处.最后,你会看 ...

  3. Boost.Asio c++ 网络编程翻译(11)

    *_at方法 这些方法在一个流上面做随机存取操作.你来指定read和write操作从什么地方開始(offset): async_read_at(stream, offset, buffer [, co ...

  4. Boost.Asio c++ 网络编程翻译(21)

    同步VS异步 Boost.Asio的作者做了一个非常惊艳的工作:它能够让你在同步和异步中自由选择,从而更好的适应你的应用. 在之前的章节中,我们学习了每种类型应用的框架,比方同步client,同步服务 ...

  5. Boost.Asio c++ 网络编程翻译(16)

    TCP异步服务端 核心功能和同步服务端的功能类似,例如以下: class talk_to_client : public boost::enable_shared_from_this<talk_ ...

  6. Boost.Asio c++ 网络编程翻译(10)

    read/write方法 这些方法对一个流进行读写操作(能够是套接字,或者其它表现的像流的类): async_read(stream, buffer [, completion],handler):这 ...

  7. Boost.Asio c++ 网络编程翻译(18)

    同步服务端 同步服务端也相当简单.它须要两个线程,一个负责接收新的client.另外一个负责处理已经存在的client. 它不能使用单线程:等带一个新的client是一个堵塞操作,所以我们须要另外一个 ...

  8. boost.asio系列——socket编程

    asio的主要用途还是用于socket编程,本文就以一个tcp的daytimer服务为例简单的演示一下如何实现同步和异步的tcp socket编程. 客户端 客户端的代码如下: #include &l ...

  9. 使用boost.asio实现网络通讯

    #include <boost/asio.hpp> #define USING_SSL //是否加密 #ifdef USING_SSL #include <boost/asio/ss ...

随机推荐

  1. 桂电在线_微信公众平台开发之-运用angularjs显示学校公告新闻列表和详情页面

    折腾angularjs的感悟 几天折腾,总的来说看了很多博客,要么不是最新的技术文档,要么写得不够完整,因为别人是基于他们的理解写的技术博客代码不一定会贴完整,所以一旦你用的是最新的想要看完整的实例就 ...

  2. Linux下安装gcc 、g++ 、gfortran编译器

    一.ubuntu下gcc/g++/gfortran的安装 1.安装 (1).gcc ubuntu下自带gcc编译器.可以通过“gcc -v”命令来查看是否安装. (2).g++ 安装g++编译器,可以 ...

  3. C语言中float,double类型,在内存中的结构(存储方式)

    C语言中float,double类型,在内存中的结构(存储方式)从存储结构和算法上来讲,double和float是一样的,不一样的地方仅仅是float是32位的,double是64位的,所以doubl ...

  4. "System.Web" 中不存在类型或命名空间

    System.Web”中不存在类型或命名空间名称script  /找不到System.Web.Extensions.dll引用 添加引用就行了...“添加引用→.Net→System.Web.Ente ...

  5. 1.1机器学习基础-python深度机器学习

    参考彭亮老师的视频教程:转载请注明出处及彭亮老师原创 视频教程: http://pan.baidu.com/s/1kVNe5EJ 1. 课程介绍 2. 机器学习 (Machine Learning, ...

  6. dp 0-1背包问题

    0-1背包的状态转换方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] } f[i,j]表示在前i件物品中选择若干件放在承重为 j 的背包 ...

  7. 利用Anaconda安装python后,如何安装opencv-python

    利用Anaconda安装python后,想要安装opencv-python,但发现利用opencv-python的官方教程,没法实现opencv的安装 还好看到了另外一篇博客的方法,试一下,果然凑效 ...

  8. C#使用字符串分割字符串

    我们都会用字符分割字符串: string[] recvArr = recv.Split(';'); 如果用字符串分割呢?下面: string[] sArray = Regex.Split(recv, ...

  9. c#回调函数写法

    添加一个cs文件,在里面定义回调 using System; using System.Collections.Generic; using System.Linq; using System.Web ...

  10. 3.2 java中堆栈(stack)和堆(heap)(还在问静态变量放哪里,局部变量放哪里,静态区在哪里.....进来)

    (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编 译时就可以给 ...