zoukankan      html  css  js  c++  java
  • 二、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 操作系统才有
  • 相关阅读:
    为什么一段时间后网站后台自动退出 php中session过期时间设置
    php中的$_GET如何获取带有“#”的参数
    让页面在打开时自动刷新
    MySQL关联left join 条件on与where不同
    图形上下文的栈操作(保存和恢复)
    图形上下文的矩阵操作(平移-缩放-旋转)
    Quartz2D内存管理
    文字绘制-图片绘制-水印绘制思路
    UIBezierPath-完善曲线
    UIBezierPath-绘制基本图形
  • 原文地址:https://www.cnblogs.com/ylcc-zyq/p/14958102.html
Copyright © 2011-2022 走看看