二、asio网络编程

TCP,UDP和ICMP

Asio对TCP,UDP,ICMP协议提供现成的支持。

TCP客户端

使用解析器执行主机名解析,其中查找主机和服务名称并将其转换为一个或多个端点:

ip::tcp::resolver resolver(my_io_context);
ip::tcp::resolver::query query("www.boost.org", "http");
ip::tcp::resolver::iterator iter = resolver.resolve(query);
ip::tcp::resolver::iterator end; // End marker.
while (iter != end)
{
  ip::tcp::endpoint endpoint = *iter++;
  std::cout << endpoint << std::endl;
}

上面获得的端点列表可以同时包含 IPv4 和 IPv6 端点,因此程序应该测试每一个,直到找到一个有效的。这使客户端程序与 IP 版本无关。

为了简化协议无关程序的开发,TCP客户端可以使用自由函数connect()async_connect()来建立连接。这些函数尝试链表中的每一个端口直到套接字成功连接。如下调用所示:

ip::tcp::socket socket(my_io_context);
boost::asio::connect(socket,resolver.resolve(query));

将同步尝试所有端点,直到成功连接一个。 类似地,异步连接可以通过以下方式执行:

boost::asio::async_connect(socket_, iter,
    boost::bind(&client::handle_connect, this,
      boost::asio::placeholders::error));

// ...

void handle_connect(const error_code& error)
{
  if (!error)
  {
    // Start read or write operations.
  }
  else
  {
    // Handle error.
  }
}

当指定端点可用时,可以创建并连接套接字:

ip::tcp::socket socket(my_io_context);
socket.connect(endpoint);

可以使用 receive()async_receive()send() async_send()成员函数从连接的 TCP 套接字读取或写入数据。 但是,由于这些可能导致短写入或短读,应用程序通常会使用以下操作代替:read()async_read()write() async_write()

TCP服务端

程序使用acceptor来接收到来的TCP连接。

ip::tcp::acceptor acceptor(my_io_context, my_endpoint);
...
ip::tcp::socket socket(my_io_context);
acceptor.accept(socket);

成功接受套接字后,就可以按上面所说的 TCP 客户端读取或写入它。

UDP

UDP的主机解析也是通过解析器:

ip::udp::resolver resolver(my_io_context);
ip::udp::resolver::query query("localhost", "daytime");
ip::udp::resolver::iterator iter = resolver.resolve(query);
...

UDP 套接字通常绑定到本地端点。 以下代码将创建一个 IPv4的 UDP 套接字并将其绑定到“any”地址上的12345端口:

ip::udp::endpoint endpoint(ip::udp::v4(), 12345);
ip::udp::socket socket(my_io_context, endpoint);

可以使用receive_from()、async_receive_from()、send_to()async_send_to() 成员函数从未连接的 UDP 套接字读取或写入数据。 对于已连接的 UDP 套接字,请使用 receive()、async_receive()、send() async_send() 成员函数。

ICMP

与 TCP 和 UDP 一样,ICMP 主机名解析是使用解析器执行的:

ip::icmp::resolver resolver(my_io_context);
ip::icmp::resolver::query query("localhost", "");
ip::icmp::resolver::iterator iter = resolver.resolve(query);
...

ICMP 套接字可以绑定到本地端点。 以下代码将创建一个 IPv6 的ICMP 套接字并将其绑定到“any”地址:

ip::icmp::endpoint endpoint(ip::icmp::v6(), 0);
ip::icmp::socket socket(my_io_context, endpoint);

ICMP协议不需要使用端口号。

可以使用receive_from()、async_receive_from()、send_to()async_send_to() 成员函数从未连接的 ICMP 套接字读取或写入数据。

对其他协议的支持

可以通过实现协议类型要求来添加对其他套接字协议(例如蓝牙或 IRCOMM 套接字)的支持。然而,在许多情况下,这些协议也可以与 Boost.Asio 的通用协议支持一起使用。 为此,Boost.Asio 提供了以下四个类:

  • generic::datagram_protocol
  • generic::raw_protocol
  • generic::seq_packet_protocol
  • generic::stream_protocol

这些类都实现了协议类型要求,不过可以让用户在运行时指定地址族(例如AF_INET)和协议族(例如IPROTO_TCP)。例如:

boost::asio::generic::stream_protocol::socket my_socket(my_io_context);
my_socket.open(boost::asio::generic::stream_protocol(AF_INET, IPPROTO_TCP));
...

包括一个端点类模板 boost::asio::generic::basic_endpoint 以支持这些协议类。此端点可以保存任何其他端点类型,前提是其本机表示匹配sockaddr_storage 对象。 此类还将从实现端点类型要求的其他类型转换:

boost::asio::ip::tcp::endpoint my_endpoint1 = ...;
boost::asio::generic::stream_protocol::endpoint my_endpoint2(my_endpoint1);

转换是隐式的,以支持以下用例:

boost::asio::generic::stream_protocol::socket my_socket(my_io_context);
boost::asio::ip::tcp::endpoint my_endpoint = ...;
my_socket.connect(my_endpoint);//发生上面的转换

C++11移动构造

使用 C++11 时,可以从套接字(或接受器)对象执行移动构造以转换为更通用的协议的套接字(或接受器)类型。 如果协议转换有效:

Protocol1 p1 = ...;
Protocol2 p2(p1);

那么对应的套接字也允许转换:

Protocol1::socket my_socket1(my_io_context);
...
Protocol2::socket my_socket2(std::move(my_socket1));

例如,一种可能的转换是从 TCP 套接字到通用的面向流的套接字:

boost::asio::ip::tcp::socket my_socket1(my_io_context);
...
boost::asio::generic::stream_protocol::socket my_socket2(std::move(my_socket1));

这些转换也可用于移动赋值。

这些转换不限于上述通用协议类。 用户定义的协议可以通过类似protocol1 到protocol2 的有效转换来利用此功能。

接受通用套接字

为方便起见,套接字接受器的 accept() async_accept()函数可以直接接受不同协议的套接字类型,前提是相应的协议转换有效。 例如,支持以下内容,因为协议 boost::asio::ip::tcp 可转换为 boost::asio::generic::stream_protocol

boost::asio::ip::tcp::acceptor my_acceptor(my_io_context);
...
boost::asio::generic::stream_protocol::socket my_socket(my_io_context);
my_acceptor.accept(my_socket);

socket输入输出流

Asio 含有在套接字上实现iostreams的类。 这些隐藏了与端点解析、协议独立性等相关的复杂性。要创建连接,可以简单地编写:

ip::tcp::iostream stream("www.boost.org", "http");
if (!stream)
{
  // Can't connect.
}

iostream 类还可以与接受器结合使用来创建简单的服务器。 例如:

io_context ioc;

ip::tcp::endpoint endpoint(tcp::v4(), 80);
ip::tcp::acceptor acceptor(ioc, endpoint);

for (;;)
{
  ip::tcp::iostream stream;
  acceptor.accept(stream.socket());
  ...
}

超时可以通过调用expires_at()expires_from_now() 来设置截止日期。 超过截止时间发生的任何套接字操作都会将 iostream 置于“bad”状态。

例如,一个简单的客户端程序是这样的:

ip::tcp::iostream stream;
stream.expires_from_now(boost::posix_time::seconds(60));
stream.connect("www.boost.org", "http");
stream << "GET /LICENSE_1_0.txt HTTP/1.0
";
stream << "Host: www.boost.org
";
stream << "Accept: */*
";
stream << "Connection: close

";
stream.flush();
std::cout << stream.rdbuf();

如果所有套接字操作的总时间超过 60 秒,则将失败。

如果确实发生了错误,可以使用 iostream 的 error() 成员函数从最近的系统调用中检索错误代码:

if (!stream)
{
  std::cout << "Error: " << stream.error().message() << "
";
}

注意:这些 iostream 模板仅支持 char,不支持 wchar_t,并且不执行任何代码转换。

BSD套接字API与Asio

Asio 库包括一个基于 BSD 套接字 API 的低级套接字接口。它还用作其他语言(如 Java)中网络 API 的基础。此低级接口旨在支持高效且可扩展的应用程序的开发。例如,它允许程序员更好地控制系统调用的数量,避免冗余数据复制,最大限度地减少线程等资源的使用等。

不包括 BSD 套接字 API 的不安全和容易出错的方面。 例如,使用 int 来表示所有套接字就缺乏类型安全性。 Boost.Asio 中的套接字表示为每个协议使用不同的类型,例如 TCP 使用 ip::tcp::socket,UDP 使用 ip::udp::socket

下表示BSD的套接字API与对应Asio函数之间的映射:

BSD套接字API元素 Asio对应内容
套接字描述符-POSIX为int,wndows为SOCKET TCP:ip::tcp::socket,ip::tcp::acceptor;
UDP:ip::udp::socket
basic_socket,basic_stream_socket,basic_datagram_socket,basic_raw_socket
in_addr,in6_addr ip::addres,ip::address_v4,ip::address_v6
socketaddr_in,sockaddr_in6 TCP:ip::tcp::endpoint;
UDP:ip::udp::endpoint;
ip::basic_endpoint
accept() TCP:ip::tcp::acceptor::accept()
basic_socket_acceptor::accept()
bind() TCP:ip::tcp::acceptor::bind(),ip::tcp::socket::bind();
UDP:ip::udp::socket::bind();basic_socket::bind()
close() TCP:ip::tcp::acceptor::close(),ip::tcp::socket::close();
UDP:ip::udp::socket::close();
basic_socket::close()
connect() TCP: ip::tcp::socket::connect()
UDP: ip::udp::socket::connect()
basic_socket::connect()
getaddrinfo(), gethostbyaddr(), gethostbyname(), getnameinfo(), getservbyname(), getservbyport() For TCP: ip::tcp::resolver::resolve(), ip::tcp::resolver::async_resolve()For UDP: ip::udp::resolver::resolve(), ip::udp::resolver::async_resolve()ip::basic_resolver::resolve(), ip::basic_resolver::async_resolve()
gethostname() ip::host_name()
getpeername() For TCP: ip::tcp::socket::remote_endpoint()
For UDP: ip::udp::socket::remote_endpoint()
basic_socket::remote_endpoint()
getsockname() For TCP: ip::tcp::acceptor::local_endpoint(), ip::tcp::socket::local_endpoint()
For UDP: ip::udp::socket::local_endpoint()
basic_socket::local_endpoint()
getsockopt() For TCP: ip::tcp::acceptor::get_option(), ip::tcp::socket::get_option()
For UDP: ip::udp::socket::get_option()
basic_socket::get_option()
inet_addr(), inet_aton(), inet_pton() ip::address::from_string(), ip::address_v4::from_string(), ip_address_v6::from_string()
inet_ntoa(), inet_ntop() ip::address::to_string(), ip::address_v4::to_string(), ip_address_v6::to_string()
ioctl() For TCP: ip::tcp::socket::io_control()
For UDP: ip::udp::socket::io_control()
basic_socket::io_control()
listen() For TCP: ip::tcp::acceptor::listen()
basic_socket_acceptor::listen()
poll(), select(), pselect() io_context::run(), io_context::run_one(), io_context::poll(), io_context::poll_one()
readv(), recv(), read() For TCP: ip::tcp::socket::read_some(), ip::tcp::socket::async_read_some(), ip::tcp::socket::receive(), ip::tcp::socket::async_receive()
For UDP: ip::udp::socket::receive(), ip::udp::socket::async_receive()
basic_stream_socket::read_some(), basic_stream_socket::async_read_some(), basic_stream_socket::receive(), basic_stream_socket::async_receive(), basic_datagram_socket::receive(), basic_datagram_socket::async_receive()
recvfrom() For UDP: ip::udp::socket::receive_from(), ip::udp::socket::async_receive_from()
basic_datagram_socket::receive_from(), basic_datagram_socket::async_receive_from()
send(), write(), writev() For TCP: ip::tcp::socket::write_some(), ip::tcp::socket::async_write_some(), ip::tcp::socket::send(), ip::tcp::socket::async_send()
For UDP: ip::udp::socket::send(), ip::udp::socket::async_send()
basic_stream_socket::write_some(), basic_stream_socket::async_write_some(), basic_stream_socket::send(), basic_stream_socket::async_send(), basic_datagram_socket::send(), basic_datagram_socket::async_send()
sendto() For UDP: ip::udp::socket::send_to(), ip::udp::socket::async_send_to()
basic_datagram_socket::send_to(), basic_datagram_socket::async_send_to()
setsockopt() For TCP: ip::tcp::acceptor::set_option(), ip::tcp::socket::set_option()
For UDP: ip::udp::socket::set_option()
basic_socket::set_option()
shutdown() For TCP: ip::tcp::socket::shutdown()
For UDP: ip::udp::socket::shutdown()
basic_socket::shutdown()
sockatmark() For TCP: ip::tcp::socket::at_mark()
basic_socket::at_mark()
socket() For TCP: ip::tcp::acceptor::open(), ip::tcp::socket::open()
For UDP: ip::udp::socket::open()
basic_socket::open()
socketpair() local::connect_pair()
Note: POSIX 操作系统才有
原文地址:https://www.cnblogs.com/ylcc-zyq/p/14958102.html