同步VS异步

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

每个你都能够作为你应用的基础。假设要更加深入地学习各种类型应用的细节,请继续。


混合同步异步编程
Boost.Asio库同意你进行同步和异步的混合编程。我个人觉得这是一个坏主意,可是Boost.Asio(就像C++一样)在你须要的时候同意你深入底层。

通常来说,当你写一个异步应用时。你会非常easy掉入这个陷阱。比方在响应一个异步write操作时,如果。你做了一个同步read操作:
io_service service;
ip::tcp::socket sock(service);
ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 8001);
void on_write(boost::system::error_code err, size_t bytes) {
     char read_buff[512];
     read(sock, buffer(read_buff));
}
   async_write(sock, buffer("echo"), on_write);
毫无疑问。同步read操作会堵塞当前的线程,从而导致其它不论什么正在等待的异步操作变成挂起状态(对这个线程)。这是一段糟糕的代码。由于它会导致整个应用变得无响应或者整个被堵塞掉(全部异步执行的端点都必须避免堵塞,而执行一个同步的操作违反了这个原则)。
当你写一个同步应用时。你不大可能运行异步的read或者write操作。由于同步地思考已经意味着用一种线性的方式思考(运行A。然后运行B,再运行C。等等)。
我唯一能想到的同步和异步同一时候工作的场景就是同步操作和异步操作是全然隔离的,比方,同步和异步从一个数据库进行读写。

从client传递信息到服务端VS从服务端传递信息到client
成功的client/服务端应用一个非常重要的部分就是来回传递消息(服务端到client和client到服务端)。

你须要指定用什么来标记一个消息。换句话说。当读取一个输入的消息时,你怎么推断它被完整读取了?

标记消息结尾的方式全然取决与你(标记消息的開始非常easy,由于它就是前一个消息之后传递过来的第一个字节),可是要保证消息是简单且连续的。
你能够:
  • 消息大小固定(这不是一个非常好的主意,假设我们须要发送很多其它的数据怎么办?)
  • 通过一个特殊的字符标记消息的结尾,比方’\n’或者’\0’
  • 再消息的头部指定消息的大小
我在整本书中间採用的方式都是“使用’\n’标记消息的结尾”。所以。每次读取一条消息都会例如以下:
  1. char buff_[512];
    // 同步读取
    read(sock_, buffer(buff_),
               boost::bind(&read_complete, this, _1, _2));
    // 异步读取
       async_read(sock_, buffer(buff_),MEM_FN2(read_complete,_1,_2), MEM_FN2(on_read,_1,_2));
    
       size_t read_complete(const boost::system::error_code & err, size_t
    bytes) {
           if ( err) return 0;
    already_read_ = bytes;
    bool found = std::find(buff_, buff_ + bytes, '\n') < buff_ +
       bytes;
    // 一个一个读,直到读到回车。无缓存
    return found ? 0 : 1;

    }

我把在消息头部指定消息长度这样的方式作为一个练习留给读者。它是很easy的。

client应用中的同步I/O
同步client一般都能归类到例如以下两种情况中的一种:
  • 它向服务端请求一些东西。读取结果,然后处理它们。然后请求一些其它的东西。然后一直持续下去。其实。这非常像之前章节里说到的同步client。
  • 从服务端读取消息。处理它,然后写回结果。然后读取另外一条消息,然后一直持续下去。

两种情况都使用“发送请求-读取结果”的策略。

换句话说。一个部分发送一个请求到另外一个部分然后另外一个部分返回结果。这是实现client/服务端应用很easy的一种方式,同一时候这也是我很推荐的一种方式。

你能够创建一个Mambo Jambo类型的client服务端应用,你能够随心所欲地写它们中间的不论什么一个部分,可是这会导致一场灾难。(你怎么知道当client或者服务端堵塞的时候会发生什么?)。

上面的情况看上去会比較相似,可是它们很不同:
  • 前者,服务端响应请求(服务端等待来自client的请求然后回应)。这是一个请求式连接,client从服务端拉取它须要的东西。
  • 后者,服务端发送事件到client然后由client响应。这是一个推式连接,服务端推送通知/事件到client。

你大部分时间都在做请求式client/服务端应用。这也是比較简单。同一时候也是比較常见的。

你能够把拉取请求(client到服务端)和推送请求(服务端到client)结合起来,可是。这是很复杂的,所以你最好避免这样的情况。把这两种方式结合的问题在于:假设你使用“发送请求-读取结果”策略。

就会发生系以下一系列事情:

client写入(发送请求)
服务端写入(发送通知到client)
client读取服务端写入的内容。然后将其作为请求的结果进行解析
服务端堵塞以等待client的返回的结果,这会在client发送新请求的时候发生
服务端把发送过来的请求当作它等待的结果进行解析
client会堵塞(服务端不会返回不论什么结果,由于它把client的请求当作它通知返回的结果)
在一个请求式client/服务端应用中,避免上面的情况是很easy的。你能够通过实现一个ping操作的方式来模拟一个推送式请求。我们假设每5秒钟clientping一次服务端。

假设没有事情须要通知。服务端返回一个类似ping ok的结果。假设有事情须要通知。服务端返回一个ping [event_name]。然后client就能够初始化一个新的请求去处理这个事件。

复习一下,第一种情况就是之前章节中的同步client应用。它的主循环例如以下:
void loop() {
// 对于我们登录操作的结果
write("login " + username_ + "\n");
read_answer();
while ( started_) {
           write_request();
read_answer();
...

} }

我们对其进行改动以适应另外一种情况:
void loop() {
while ( started_) {
           read_notification();
           write_answer();
}
   }
void read_notification() {
       already_read_ = 0;
read(sock_, buffer(buff_),
               boost::bind(&talk_to_svr::read_complete, this, _1, _2));
process_notification();
   }
void process_notification() {
     // ... 看通知是什么,然后准备回复
}

PS:新的迭代再次来袭…






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

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

    异步服务端 这个图表是相当复杂的:从Boost.Asio出来你能够看到4个箭头指向on_accept.on_read,on_write和on_check_ping. 着也就意味着你永远不知道哪个异步调 ...

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

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

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

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

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

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

  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. Larazrus 快捷键 总结。

    delphi+cnpack 用惯了,转移到 lazarus 有点难受是不是!其实, lazaurs 的编辑器也是蛮强 大的,支持代码补全,自动完成,模板编辑,多行缩进注释,选定代码后批量更改里面的单 ...

  2. 《深入理解Android2》读书笔记(一)

    2017-5-12 从今天开始估计有一段空闲时间,开始阅读<深入理解Android2>,并写读书笔记. 第一章搭建环境直接略过. 第二章是Binder,暂时略过 7大类服务包括:1.And ...

  3. 从零开始做SSH项目(一)

    1.数据库脚本 用户表 CREATE TABLE `ybl`.`userinfo`( `id` INT NOT NULL AUTO_INCREMENT, `email` ) NOT NULL, `id ...

  4. CNN的发展

    模型的建立过程: 1959年,Hubel & Wiesel发现动物视觉皮层中的细胞负责检测感受野(receptive fields)中的光线.论文:Receptive fields and f ...

  5. 关于 Unity WebGL 的探索(二)

    关于 Unity WebGL 的探索(二) 上一篇博客记录了关于 WebGL 移植的第一步:部分 C/C++ 插件的编译,目前项目中的部分插件使用该方法通过,接下来比较大的一部分工作量是网络模块 We ...

  6. 洛谷——P3908 异或之和

    P3908 异或之和 题目描述 求1 \bigoplus 2 \bigoplus\cdots\bigoplus N1⨁2⨁⋯⨁N 的值. A \bigoplus BA⨁B 即AA , BB 按位异或. ...

  7. Sqli-labs介绍、下载、安装

    SQLI和sqli-labs介绍 SQLI,sql injection,我们称之为sql注入.何为sql,英文:Structured Query Language,叫做结构化查询语言.常见的结构化数据 ...

  8. DBCS 从256开始

    ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字 ...

  9. 分享Kali Linux 2017年第31周镜像文件

     分享Kali Linux 2017年第31周镜像文件  Kali Linux官方于7月30日发布2017年的第31周镜像.这次维持了11个镜像文件的规模.默认的Gnome桌面的4个镜像,E17.KD ...

  10. 【贪心】【线性基】bzoj2460 [BeiJing2011]元素

    题意:让你求一些数在XOR下的带权极大无关组. 带权极大无关组可以用贪心,将这些数按权值从大到小排序之后,依次检验其与之前的数是否全都线性无关.可以用线性基来搞. 可以用拟阵严格证明,不过也可以脑补一 ...