*_at方法
这些方法在一个流上面做随机存取操作。你来指定read和write操作从什么地方開始(offset):
async_read_at(stream, offset, buffer [, completion], handler):这种方法在一个指定的流上从offset处開始运行一个异步的read操作,当操作结束时,他会调用handler。

handler的格式为:void handler(const boost::system::error_code&  err,
size_t bytes);。buffer能够是普通的wrapper()包装或者streambuf方法。假设你指定一个completion方法。它会在每次read操作成功之后调用,然后告诉Boost.Asio async_read_at操作已经完毕(假设没有,则继续读取)。它的格式为:size_t
 completion(const boost::system::error_code& err, size_t bytes);。

当completion方法返回0时。我们觉得read操作完毕了;假设返回一个非零值。

它代表了下一次调用流的async_read_some_at方法的最大读取字节数。

async_write_at(stream, offset, buffer [, completion], handler):这种方法运行一个异步的write操作。參数的意义和async_read_at是一样的
read_at(stream, offset, buffer [, completion]):这种方法在一个运行的流上,指定的offset处開始read。

參数的意义和async_read_at是一样的

write_at(stream, offset, buffer [, completion]):这种方法在一个运行的流上,指定的offset处開始write。參数的意义和async_read_at是一样的
这些方法不支持套接字。它们用来处理流的随机訪问。也就是说,流是能够随机訪问的。套接字显然不是这样(套接字是不可返回的)
以下这个样例告诉你怎么从一个文件偏移为256的位置读取128个字节:
io_service service;
int main(int argc, char* argv[]) {
HANDLE file = ::CreateFile("readme.txt", GENERIC_READ, 0, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
0);
windows::random_access_handle h(service, file);
streambuf buf;
read_at(h, 256, buf, transfer_exactly(128));
std::istream in(&buf);
std::string line;
std::getline(in, line);
std::cout << "first line: " << line << std::endl;
}
异步编程
这个部分对你在进行异步编程时可能碰到的一些问题进行了深入的探究。再读了一遍之后。我建议你在接下来学习这本书的时候,常常回过头来再读读,从而增强你对这部分的理解。

异步的需求
就像我之前所说的,同步编程比异步编程简单非常多。

这是由于,线性的思考是非常easy的(调用A,调用A结束,调用B,调用B结束。然后继续,这是以事件处理的方式来思考)。

在后面的你会碰到这样的情况,比方:五件事情,你不知道它们运行的顺序。也不知道他们是否会运行!

虽然异步编程更难。可是你会更倾向于它。比方:写一个须要处理非常多并发訪问的服务端。

并发訪问越多,异步编程就比同步编程越简单。

如果:你有一个软件处理1000个并发訪问。每一个信息都从client发给服务端。然后再返回给client,以‘\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 ...
}
有一件事情是你在不论什么服务端(和不论什么基于网络的软件)都须要避免的,就是代码不再响应。在我们样例里,我们须要handle_ clients()方法尽可能少的堵塞。假设放在在不论什么点上堵塞,不论什么进来的信息都须要等待方法解除堵塞然后再去处理它们。

为了保持响应,我们仅仅在一个套接字有数据的时候我们才读,也就是说。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()方法很重要,你想要一步实现“測试读取然后标记为读取中”。假设你有两步(“測试读取”和“标记为读取中”)。你可能会有两个线程同一时候为一个client做測试读取操作,然后你会有两个线程同一时候为一个client调用on_read。结果就是数据冲突甚至可能导致软件崩溃。

你会发现代码变得极其复杂。
同步编程有第三个选择,就是为每一个连接开辟一个线程。

当时当并发的线程添加时。这就变成了一个最不能出现的情况。

然后,让我们来看异步编程。

我们不断的异步读取。当一个client请求某些东西时,on_read被调用,然后回应。然后等待下一个请求(然后開始另外一个异步的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")
Boost.Asio Fundamentals
[ 44 ]
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结构里面唯独两个成员。handle_clients()只调用了async_read_until。然后它创建了10个线程,每一个都调用service.run()。这些线程会处理不论什么来自客户端的异步read操作。然后分发不论什么向客户端的异步write操作。另外一件须要注意的事情是:on_read()一直在为下一次异步read操作做准备(看最后一行代码)

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

  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++ 网络编程翻译(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. asp.net几种开源上传控件,flash,ajax版,支持多文件

    原文发布时间为:2010-03-18 -- 来源于本人的百度文章 [由搬家工具导入] 1、AspnetUpload 地址:http://www.aspnetupload.net/ 最早接触的上传控件。 ...

  2. quartz的配置

    Quartz.Net中的概念:计划者(IScheduler).工作(IJob).触发器(Trigger).给计划者一个工作,让他在Trigger(什么条件下做这件事)触发的条件下执行这个工作 将要定时 ...

  3. uva 1149:Bin Packing(贪心)

    题意:给定N物品的重量,背包容量M,一个背包最多放两个东西.问至少多少个背包. 思路:贪心,最大的和最小的放.如果这样都不行,那最大的一定孤独终生.否则,相伴而行. 代码: #include < ...

  4. 线程之间通信 等待(wait)和通知(notify)

    线程通信概念: 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程之间的通信就成为整体的必用方式之一.当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同 ...

  5. pylint & jenkins

    利用pylint 检测 python源码. Pylint 提供了简单的方式来分析 Python 代码,其高可配置性很容易使一个部门的人员使用统一的代码风格. Pylint 是什么 Pylint 是一个 ...

  6. AC日记——石子归并 51nod 1021

    石子归并 思路: 经典动态规划——归并类问题: 我们把状态划为n个,即1-n的n个长度为n个状态: 那么,每个长度为i的状态都可以由i-1个长度为i-1的状态推出: 所以,dp转移方程: dp[i][ ...

  7. iOS直播Liveroom组件,游客,用户多次切换登录同一直播间,消息出现多次重复问题解决

    byzqk 新版,加入连麦功能,直播的流程修改很多,每次登录都需要登录liveroom组件 期间遇到一个奇葩的问题,就是游客登录组件之后,切换为用户登录,出现im消息重复的问题,一开始以为是游客退出不 ...

  8. js中click重复执行

    问题背景:在写一个非常简单添加方法的时候,用onclick事件调用添加方法,点击第一次没问题,第二次会重复执行,经过多次查找资料得知这个状况的解决方案,特意总结一下: 代码如下:点击#spec_for ...

  9. 搭建https本地服务器:如何得到被所有客户端认可的ssl证书

    https,作为http的加密版,作用还是很大的:能够提升网站搜索权重,让你的网站更安全,而且如果你的网站没有使用https的话,将无法作为移动设备原生应用的api接口.可见掌握为网站启用https的 ...

  10. 洛谷 P1579 哥德巴赫猜想(升级版)【筛素数/技巧性枚举/易错】

    [链接]:https://www.luogu.org/problemnew/show/P1579 题目背景 1742年6月7日哥德巴赫写信给当时的大数学家欧拉,正式提出了以下的猜想:任何一个大于9的奇 ...