首页 > 代码库 > Boost.Asio c++ 网络编程翻译(25)
Boost.Asio c++ 网络编程翻译(25)
代理实现
代理一般位于客户端和服务端之间。它接受客户端的请求,可能对请求进行修改,然后接着把请求发送到服务端。然后从服务端取回结果,也可能对结果进行修改,然后接着把结果发送到客户端。
代理有什么特别的,我们讲述它的目的在于:对每个连接,你都需要两个sokect,一个给客户端,另外一个给服务端。这些都给实现一个代理增加了不小的难度。
实现一个同步的代理应用比异步的方式更加复杂;数据可能同时从两个端过来(客户端和服务端),也可能同时发往两个端。这也就意味着如果我们选择同步,我们就可能在一端向另一端read()或者write(),同时另一端向这一端read()或者write()时阻塞,这也就意味着最终我们会变的无响应。
把下面几条当作一个异步代理的简单例子:
- 在我们的情形下,我们在构造函数中能拿到两个连接。但是不是所有的都这样,比如对于一个web代理来说,客户端只告诉我们服务端的地址。
- 因为比较简单,所以不是线程安全的。参考如下的代码:
class proxy : public boost::enable_shared_from_this<proxy> {
proxy(ip::tcp::endpoint ep_client, ip::tcp::endpoint ep_
server) : ... {}
public:
static ptr start(ip::tcp::endpoint ep_client,
ip::tcp::endpoint ep_svr) {
ptr new_(new proxy(ep_client, ep_svr));
// … 连接到两个端
return new_;
}
void stop() {
// ... 关闭两个连接
}
bool started() { return started_ == 2; }
private:
void on_connect(const error_code & err) {
if ( !err) {
if ( ++started_ == 2) on_start();
} else stop();
}
void on_start() {
do_read(client_, buff_client_);
do_read(server_, buff_server_);
}
... private:
ip::tcp::socket client_, server_;
enum { max_msg = 1024 };
char buff_client_[max_msg], buff_server_[max_msg];
int started_;
};
这是个非常简单的代理。当我们两个端都连接时,它开始从两个端读取(on_start()方法):
class proxy : public boost::enable_shared_from_this<proxy> {
...
void on_read(ip::tcp::socket & sock, const error_code& err, size_t
bytes) {
char * buff = &sock == &client_ ? buff_client_ : buff_server_;
do_write(&sock == &client_ ? server_ : client_, buff, bytes);
}
void on_write(ip::tcp::socket & sock, const error_code &err,
size_t bytes){
if ( &sock == &client_) do_read(server_, buff_server_);
else do_read(client_, buff_client_);
}
void do_read(ip::tcp::socket & sock, char* buff) {
async_read(sock, buffer(buff, max_msg),
MEM_FN3(read_complete,ref(sock),_1,_2),
MEM_FN3(on_read,ref(sock),_1,_2));
}
void do_write(ip::tcp::socket & sock, char * buff, size_t size) {
sock.async_write_some(buffer(buff,size),
MEM_FN3(on_write,ref(sock),_1,_2));
}
size_t read_complete(ip::tcp::socket & sock,
const error_code & err, size_t bytes) {
if ( sock.available() > 0) return sock.available();
return bytes > 0 ? 0 : 1;
}
};
对每一个成功的读取操作(on_read),它把消息发到另外一个部分。只要消息一发送成功(on_write),我们就从来源那部分再次读取。
使用下面的代码片段让这个流程运转起来:
int main(int argc, char* argv[]) {
ip::tcp::endpoint ep_c( ip::address::from_string("127.0.0.1"),
8001);
ip::tcp::endpoint ep_s( ip::address::from_string("127.0.0.1"),
8002);
proxy::start(ep_c, ep_s);
service.run();
}
你会注意到我在读和写中我重用了buffer。这个重用是ok的,因为从客户端读取的消息是在一个新消息被读取之前写到服务端的,反之亦然。这也意味着这种实现方式会碰到无响应的问题。当我们正在处理到B部分的写入时,我们不会从A读取(我们会在写入到B部分完成时重新从A部分读取)。你可以通过下面的方式重写实现来克服这个问题:
你需要使用多个读取buffer
对每个成功的read操作,除了异步写回到另外一个部分,还需要做额外的一个read(写入到一个新的buffer)
对每个成功的write操作,销毁(或者重用)这个buffer
我会把这个当作联系留给你们。
小结
当在选择同步或者异步时需要考虑很多事情。最先需要考虑的就是避免混淆它们。
在这一章中,我们已经学到:
- 实现,测试,调试每个类型的应用是多么简单
- 线程是如何影响你的应用的
- 应用的行为是怎么影响它的实现的(拉取或者推送类型)
- 当你选择异步时你怎样去嵌入你自己的异步操作
接下来,我们会了解一些Boost.Asio不那么为人知晓的特性,而且我最喜欢的Boost.Asio的特性-协程,它可以使你享受异步带来的好处而几乎不会碰到它任何的坏处。
Boost.Asio c++ 网络编程翻译(25)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。