Boost.Asio有三种类型的Socket类: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类创建一个对应的套接字。在io_service实例构造的时候,你需要传入这个socket:
io_service service;ip::udp::socket sock(service); sock.set_option(ip::udp::socket::reuse_address(true));123
每一个socket的名字都由typedef而来:
ip::tcp::socket = basic_stream_socket
ip::udp::socket = basic_stream_socket
ip::icmp::socket = basic_stream_socket
所有的同步函数都有例外情况,它们会抛出异常或者返回一个错误代码,例如下列代码:
sync_func(arg1, arg2 ... argN); //抛出异常 boost::system::error_code ec; sync_func(arg1 arg2, ... , argN, ec); //返回错误代码123
在后面,会遇到很多同步函数。为了保持简洁,后面会忽略返回错误代码的例外情况。
这些函数被分为几个组。并不是每个种类的socket都能访问所有这些成员函数。本节的最后,会有一张表列出哪些成员是隶属于何种socket类。
需要注意的是,所有的异步函数都是立即返回的,而同步版本的函数,只在操作完成之后才返回。
有些函数用于连接或者绑定socket,断开连接,查询连接是否有效:
assign(protocol, socket):它给socket实例赋予原始套接字(原生)。使用它来处理一些遗留系统(这些系统中套接字通常是已经创建过了的)。
open(protocol):用给定的IP协议(v4或者v6)打开一个socket。主要用于UDP/ICMP socket,或者是用于服务器端socket。
bind(endpoint):绑定到指定的地址。
connect(endpoint):同步连接到指定的地址。
async_connect(endpoint):异步地连接到指定的地址。
is_open():socket是否是打开的。
close():关闭套接字。任何在此socket的异步操作都会被取消掉,并以error::operation_aborted错误代码完成。
shutdown(type_of_shutdown):禁用send,receive操作。
cancel():取消socket上的所有异步操作。所有在此socket上的异步操作都会被立即完成,并以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()); sock.connect(ep); sock.write_some(buffer("GET /index.html\r\n")); char buff[1204]; sock.read_some(buffer(buff, 1024)); sock.shutdown(ip::tcp::socket::shutdown_receive); sock.close();123456789
读写函数在socket上进行I/O操作。
对于异步函数,handler是一个回调函数,形如void handler(const boost::system::error_code& e, size_t bytes)。
async_receive(buffer, [flags], handler):在socket上开始异步的recevie操作。
async_read_some(buffer, handler):和async_receive作用相同。
async_receive_from(buffer, endpoint [,flags], handler:在指定的endpoint上开始异步的receive操作。
async_send(buffer [, flags], handler):将缓冲区中的数据异步的发送出去。
async_write_some(buffer, handler):和async_send相同。
async_send_to(buffer, endpoint, handler):在指定的endpoint上开始异步的发送操作。
receive(buffer [, flags]):同步接受数据到buffer,将阻塞,知道数据接收完成,或者出错。
read_some(buffer):和receive相同。
receive_from(buffer, endpoint [, flags]):从给定的endpoint接收数据,将阻塞,知道数据接收完成,或者出错。
send(buffer [, flags]):同步的发送缓冲区中的数据,将阻塞,知道数据发送完成,或者出错。
write_some(buffer):和send相同。
send_to(buffer, endpoint [, flags]):同步地将数据发送给指定endpoint。将阻塞,知道数据发送完成,或者出错。
available():返回在不阻塞的情况下,可以从socket中读取出多少字节。
这里简单讨论下缓冲区。默认的flags参数是0,也可以混合下面的值:
ip::socket_type::socket::message_peek:这个flag表示,只在缓冲区中检索数据。它可以返回消息,但是下一次读时,会重新读取这条数据。
ip::socket_type::socket::message_out_of_band:这个标志表示处理带外数据(OBB)。OBB数据比普通的数据更重要。对OBB数据的讨论超出了本书的范围。
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);//重新读取上次读取过的数据sock.receive(buffer(buff));12345
下面的例子演示了同步和异步的区别:
例子1,在tcp socket上同步的写和读:
ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1", 80);ip::tcp::socke sock(service); sock.connect(ep); sock.write_some(buffer("GET /index.html\r\n");std::cout<< "有效字节数: "<< sock.available() <<std::endl; char buff[512]; size_t read = sock.read_some(buffer(buff));1234567
例子2,在UDP socket上异步读和写:
ip::udp::socket sock(service); sock.open(ip::udp::v4());ip::udp::endpoint receiver_ep("xxx.xxx.xxx.xxx", 80); sock.send_to(buffer("testing\n"), receiver_ep); char buff[512];ip::udp::endpoint sender_ep; sock.receive_from(buffer(buff), sender_ep);1234567
例子3,从UDP服务器socket中异步读取:
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(); return 0; }