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

Boost.Asio基本原理

这一章涵盖了你使用Boost.Asio时必须知道的一些事情。我们也将深入研究比同步编程更机警、更有乐趣的异步编程。

网络API

这一部分包含了当使用Boost.Asio编写网络应用程序时你必须知道的事情。

Boost.Asio命名空间

Boost.Asio的一切都需要包含在boost::asio的命名空间或者其子命名空间内。

boost::asio:这是核心类和函数所在的地方。重要的类有io_service和streambuf。类似read, read_at, read_until,它们的异步同性,它们的写和异步写同行等自由函数也在这里。

boost::asio::ip:这是网络通信部分所在的地方。重要的类有address, endpoint, tcp, udp和icmp,重要的自由函数有connect和async_connect。要注意的是在boost::asio::ip::tcp::socket中间,socket只是boost::asio::ip::tcp类中间的一个typedef关键字。

boost::asio::error:这个命名空间包含了当调用I/O例程时返回的错误码

boost::asio::ssl:包含了SSL处理类的命名空间

boost::asio::local:这个命名空间包含了POSIX特性的类

boost::asio::windows:这个命名空间包含了Windows特性的类

IP地址

对于IP地址的处理,Boost.Asio提供了ip::address , ip::address_v4和ip::address_v6类。

它们提供了相当多的函数。下面列出了最重要的几个:

ip::address(v4_or_v6_address):这个函数把一个v4或者v6的地址转换成ip::address

ip::address:from_string(str):这个函数根据一个IPv4地址(用.隔开的)或者一个IPv6地址(十六进制表示)创建一个地址。

ip::address::to_string():这个函数返回这个地址的友好展示。

ip::address_v4::broadcast([addr, mask]):这个函数创建了一个广播地址

ip::address_v4::any():这个函数返回一个能表示任意地址的地址。

ip::address_v4::loopback(), ip_address_v6::loopback():这个函数返回环路地址(为v4/v6协议)

ip::host_name():这个函数用string数据类型返回当前的主机名。

大多数情况你会选择用ip::address::from_string。

ip::address addr = ip::address::from_string("127.0.0.1");

如果你想要连接到一个主机名,继续读。下面的代码片断不管用:

// 抛出异常

ip::address addr = ip::address::from_string("www.yahoo.com");

端点

端点是你用某个端口连接到的一个地址。不同的类型socket有他自己的endpoint类,比如ip::tcp::endpoint、ip::udp::endpoint和ip::icmp::endpoint。

如果你想连接到本机的80端口,你可以这样做:

ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 80);

有三种方式来让你建立一个端点:

endpoint():这是默认构造函数,某些时候可以用来创建UDP/ICMP套接字。

endpoint(protocol, port):这个经常用来创建可以接受新连接的服务器端套接字。

endpoint(addr, port):这个创建了一个连接到某地址某端口的端点。

例子如下:

ip::tcp::endpoint ep1;

ip::tcp::endpoint ep2(ip::tcp::v4(), 80);

ip::tcp::endpoint ep3( ip::address::from_string("127.0.0.1), 80);

如果你想连接到一个主机(不是IP地址),你需要这样做:

// 输出 "87.248.122.122"

io_service service;

ip::tcp::resolver resolver(service);

ip::tcp::resolver::query query("www.yahoo.com", "80");

ip::tcp::resolver::iterator iter = resolver.resolve( query);

ip::tcp::endpoint ep = *iter;

std::cout << ep.address().to_string() << std::endl;

你可以用你需要的套接字类型来替换tcp。首先,创建一个你想要名字的查询,然后用resolve()函数解析它。如果成功,他会返回至少一个入口。利用返回的迭代器,或者使用第一个入口,或者迭代整个列表。

给点一个端点,可以获得他的地址,端口和IP协议(v4或者v6):

std::cout << ep.address().to_string() << ":" << ep.port()

<< "/" << ep.protocol() << std::endl;

套接字

Boost.Asio有三种类型的套接字类:ip::tcp, ip::udp和ip::icmp,当然它也是可扩展的。你可以创建你自己的socket类,尽管这相当复杂。如果你选择这样做,参照一下boost/asio/ip/tcp.hpp, boost/asio/ip/udp.hpp和boost/asio/ip/icmp.hpp。它们都是有着内部typedef关键字的超小类。

你可以把ip::tcp, ip::udp, ip::icmp类当作占位符;它们让你可以便捷地访问其他类/函数,如下所示:

ip::tcp::socket, ip::tcp::acceptor, ip::tcp::endpoint,ip::tcp::resolver, ip::tcp::iostream

ip::udp::socket, ip::udp::endpoint, ip::udp::resolver

ip::icmp::socket, ip::icmp::endpoint, ip::icmp::resolver

socket类创建一个相应的socket。而你总是在构造的时候传入io_service实例:

io_service service;

ip::udp::socket sock(service)

sock.set_option(ip::udp::socket::reuse_address(true));

每一个socket的名字都是一个typedef关键字

? ip::tcp::socket = basic_stream_socket<tcp>

? ip::udp::socket = basic_datagram_socket<udp>

? ip::icmp::socket = basic_raw_socket<icmp>

同步错误码

所有的同步函数都有抛出异常或者返回错误码的重载,比如如下的代码片段:

sync_func( arg1, arg2 ... argN); // 抛出异常

boost::system::error_code ec;

sync_func( arg1 arg2, ..., argN, ec); // 返回错误码

在这一章剩下的部分,你会见到大量的同步函数。简单起见,我省略了返回错误码的重载,但是它们是存在的。

套接字成员函数

这些函数被分成了几组。在各个类型的套接字里并不是所有的函数都可用。这个部分的结尾将有一个列表来展示哪个函数是属于哪个socket类的。

注意所有的异步函数都立刻返回,而它们相对的同步实现需要操作完成之后才能返回。

连接相关的函数

这些是连接或绑定套接字、断开套接字连接以及查询连接是活动还是非活动的函数:

assign(protocol,socket):这个函数分配了一个原生的套接字给这个套接字实例。当处理老(旧)程序时使用它(也就是说,原生套接字已经被建立了)

open(protocol):这个函数用给定的IP协议(v4或者v6)打开一个套接字。你主要在UDP/ICMP套接字,或者服务端套接字上使用。

bind(endpoint):这个函数绑定到一个地址

connect(endpoint):这个函数用同步的方式连接到一个地址

async_connect(endpoint):这个函数用异步的方式连接到一个地址

is_open():如果套接字已经打开,这个函数返回true

close():这个函数关闭套接字。这个套接字上任何的异步操作都会被立即关闭,同时返回error::operation_aborted错误码

shutdown(type_of_shutdown):这个函数立即使send或者receive操作失效,或者两者都失效。

cancel():这个函数取消这个套接字上所有的异步操作。这个套接字上任何的异步操作都会立即结束,然后返回error::operation_aborted错误码。

例子如下:

ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 80);

ip::tcp::socket sock(service);

sock.open(ip::tcp::v4()); n

sock.connect(ep);

sock.write_some(buffer("GET /index.html\r\n"));

char buff[1024]; sock.read_some(buffer(buff,1024));

sock.shutdown(ip::tcp::socket::shutdown_receive);

sock.close();

读写函数

这些是在套接字上执行I/O操作的函数

对于异步函数来说,处理程序的签名void handler(const boost::system::error_code& e, size_t bytes)都是一样的

async_receive(buffer, [flags,] handler):这个函数启动从套接字异步receive数据的操作。

async_read_some(buffer,handler):这个函数和async_receive(buffer, handler)功能一样。

async_receive_from(buffer, endpoint[, flags], handler):这个函数启动从一个指定端点异步receive数据的操作。

async_send(buffer [, flags], handler):这个函数启动了一个异步send缓冲区数据的功能。

async_write_some(buffer, handler):这个函数和async_send(buffer, handler)功能一致。

async_send_to(buffer, endpoint, handler):这个函数启动了一个异步send缓冲区数据到指定端点的功能。

receive(buffer [, flags]):这个函数异步地从所给的缓冲区读取数据。在读完所有数据或者错误出现之前,这个函数都是阻塞的。

read_some(buffer):这个函数的功能和receive(buffer)是一致的。

receive_from(buffer, endpoint [, flags]):这个函数异步地从一个指定的端点获取数据并写入到给定的缓冲区。在读完所有数据或者错误出现之前,这个函数都是阻塞的。

send(buffer [, flags]):这个函数同步地发送缓冲区的数据。在所有数据发送成功或者出现错误之前,这个函数都是阻塞的。

write_some(buffer):这个函数和send(buffer)的功能一致。

send_to(buffer, endpoint [, flags]):这个函数同步地把缓冲区数据发送到一个指定的端点。在所有数据发送成功或者出现错误之前,这个函数都是阻塞的。

available():这个函数返回有多少字节的数据可以无阻塞地进行同步读取。

稍后我们将讨论缓冲区。让我们先来了解一下标记。标记的默认值是0,但是也可以是以下几种:

ip::socket_type::socket::message_peek:这个标记只监测消息。它会返回这个消息,但是下一次读消息的调用会重新读取这个消息。

ip::socket_type::socket::message_out_of_band:这个标记处理带外(OOB)数据,OOB数据是被标记为比正常数据更重要的数据。关于OOB的讨论在这本书的内容之外。

ip::socket_type::socket::message_do_not_route:这个标记指定数据不使用路由表来发送。

ip::socket_type::socket::message_end_of_record:这个标记指定的数据标识了记录的结束。在Windows下不支持。

你最有可能使用message_peek,如果使用请参照下面的代码片段:

char buff[1024];

sock.receive(buffer(buff), ip::tcp::socket::message_peek );

memset(buff,1024, 0);

// re-reads what was previously read

sock.receive(buffer(buff) );

下面的是一些告诉你如何同步或异步地从不同类型的套接字读取数据的例子:

例子1是对一个TCP套接字进行同步的读写:

ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 80);

ip::tcp::socket sock(service);

sock.connect(ep);

sock.write_some(buffer("GET /index.html\r\n"));

std::cout << "bytes available " << sock.available() << std::endl;

char buff[512];

size_t read = sock.read_some(buffer(buff));

例子2是读一个UDP套接字进行同步的读写:

ip::udp::socket sock(service);

sock.open(ip::udp::v4());

ip::udp::endpoint receiver_ep("87.248.112.181", 80);

sock.send_to(buffer("testing\n"), receiver_ep);

char buff[512];

ip::udp::endpoint sender_ep;

sock.receive_from(buffer(buff), sender_ep);

[?注意:像上面代码片段展示的那样,使用receive_from从一个UDP套接字读取时,你需要一个默认构造的端点]

例子3是从一个UDP服务套接字中异步读取数据:

using namespace boost::asio;

io_service service;

ip::udp::socket sock(service);

boost::asio::ip::udp::endpoint sender_ep;

char buff[512];

void on_read(const boost::system::error_code & err, std::size_t

read_bytes) {

std::cout << "read " << read_bytes << std::endl;

sock.async_receive_from(buffer(buff), sender_ep, on_read);

}

int main(int argc, char* argv[]) {

ip::udp::endpoint ep( ip::address::from_string("127.0.0.1"),

8001);

sock.open(ep.protocol());

sock.set_option(boost::asio::ip::udp::socket::reuse_

address(true));

sock.bind(ep);

sock.async_receive_from(buffer(buff,512), sender_ep, on_read);

service.run();

}

套接字控制:

这些函数用来处理套接字的高级选项:

get_io_service():这个函数返回构造函数中传入的io_service实例

get_option(option):这个函数返回一个套接字的属性

set_option(option):这个函数设置一个套接字的属性

io_control(cmd):这个函数在套接字上执行一个I/O指令

这些是你可以获取/设置的套接字选项:

名字                           描述                                                                类型

broadcast                      如果为true,允许广播消息                                            bool

debug                          如果为true,启用套接字级别的调试                                    bool

do_not_route                   如果为true,则阻止路由选择只使用本地接口                            bool

enable_connection_aborted      如果为true,记录在accept()时中断的连接                              bool

keep_alive                     如果为true,会发送心跳                                              bool

linger                         如果为true,套接字会在有未发送数据的情况下挂起close()               bool

receive_buffer_size            套接字接收缓冲区大小                                                int

receive_low_watemark           规定套接字输入处理的最小字节数                                      int

reuse_address                  如果为true,套接字能绑定到一个已用的地址                            bool

send_buffer_size               套接字发送缓冲区大小                                                int

send_low_watermark             规定套接字数据发送的最小字节数                                      int

ip::v6_only                    如果为true,则只允许IPv6的连接                                      bool

每个名字代表了一个内部套接字typedef或者类。下面是对它们的使用:

ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 80);

ip::tcp::socket sock(service);

sock.connect(ep);

// TCP socket can reuse address

ip::tcp::socket::reuse_address ra(true);

sock.set_option(ra);

// get sock receive buffer size

ip::tcp::socket::receive_buffer_size rbs;

sock.get_option(rbs);

std::cout << rbs.value() << std::endl;

// set sock‘s buffer size to 8192

ip::tcp::socket::send_buffer_size sbs(8192);

sock.set_option(sbs);

[?在上述特性工作之前,套接字要被打开。否则,会抛出异常]

时间: 2024-12-16 06:24:54

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

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

第一次翻译,希望大家多多指正 实战出精华 Boost.Asio C++ 网络编程 用具体的C++网络编程例子来提升你的技能 John Torjan 用具体的C++网络编程例子来提升你的技能 Copyright ? 2013 Packt Publishing 版权所有,除了在鉴定文章或者评论中进行简单引用,如果没有经过出版者事先的书面授权,该书的任何部分都不能被转载.存储在检索系统中.或者以任何形式和方式传阅. 在这本书准备发行之前,我们已经尽我们最大的努力去保证书中信息的准确性.但是,这本书中包

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

异步服务端 这个图表是相当复杂的:从Boost.Asio出来你可以看到4个箭头指向on_accept,on_read,on_write和on_check_ping.着也就意味着你永远不知道哪个异步调用是下一个完成的调用,但是你可以确定的是它是这4个操作中的一个. 现在,我们是异步的了:我们可以继续保持单线程.接受客户端连接是最简单的部分,如下所示: ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 8001)

Boost.Asio c++ 网络编程翻译(30)[完结]

PS:至此终于完成了Boost.Asio C++ network programming一书的翻译,这是我人生第一本完整翻译的书,从开始的磕磕绊绊,到最后小有心得,我收获很多.我将把这个系列的博客进行整理和校对,希望有兴趣的人可以帮我一起,来给大家提供更好更专业的阅读体验. 句柄追踪信息到文件 默认情况下,句柄的追踪信息被输出到标准错误流(相当于std::cerr).你想把输出重定向到其他地方的可能性是非常高的.对于控制台应用,输出和错误输出都被默认输出到相同的地方,也就是控制台.但是对于一个w

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

保持活动 假如,你需要做下面的操作: io_service service; ip::tcp::socket sock(service); char buff[512]; ... read(sock, buffer(buff)); 在这个例子中,sock和buff的存在时间都必须比read()调用的时间要长.也就是说,在调用read()返回之前,它们都必须有效.这就是你期望的:你传给一个方法的所有参数在参数内部都必须有效.当我们采用异步方式时,事情会变得越复杂. io_service servi

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

Boost.Asio入门 什么是Boost.Asio 简单来说,Boost.Asio是一个跨平台的.主要用于网络和其他一些底层输入/输出编程的C++库. 计算机网络的设计方式有很多种,但是Boost.Asio的的方式远远优于它们.它在2005年就被包含进Boost,然后被广大Bosot的用户测试并在很多项目中使用,比如Remobo(http://www.remobo.com),可以让你创建你自己的即时私有网络(IPN),libtorrent(http://www.rasterbar.com/pr

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

*_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 byt

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

Boost.Asio-其他特性 这章我们讲了解一些Boost.Asio不那么为人所知的特性.标准的stream和streambuf对象有时候会更难用一些,但正如你所见.它们也有它们的益处.最后,你会看到姗姗来迟的Boost.Asio协程的入口,它能够让你的异步代码变的很易读.这是很惊人的一个特性. 标准stream和标准I/O buffer 读这一章节之前你须要对STL stream和STL streambuf对象有所了解. Boost.Asio在处理I/O操作时支持两种类型的buffer: b

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

io_service类 你应该已经发现大部分使用Boost.Asio编写的代码都会使用几个ios_service的实例.ios_service是这个库里面最重要的类:它负责和操作系统打交道,等待所有异步操作的结束,然后为每一个异步操作调用完成处理程序. 如果你选择用同步的方式来创建你的应用,你不需要考虑我将在这一节向你展示的东西. 你可以用几种不同的方式来使用io_service.在下面的例子中,我们有3个异步操作,2个socket连接和一个计时器等待: 有一个io_service和一个处理线程

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

同步VS异步 首先,异步编程和同步编程是有极大的不同的.在同步编程中,你所有的操作都是顺序执行的,比如从一个socket中读取(请求),然后写入(回应)到socket中.每一个操作操作都是阻塞的.因为操作是阻塞的,所以为了不影响主程序,当读写一个socket时,通常创建一个或多个线程来处理socket的输入/输出.因此,同步的服务端/客户端通常是多线程的. 相反的,异步编程是事件驱动的.你启动了一个操作,但是你不知道它何时会结束:你只是提供一个回调,当操作结束时,它会调用这个API,并返回操作结