zoukankan      html  css  js  c++  java
  • 二、asio使用教程--套接字

    使用TCP实现daytime协议

    同步的daytime客户端

    利用asio实现一个TCP的客户端应用。

    引入头文件

    #include <iostream>
    #include <boost/array.hpp>
    #include <boost/asio.hpp>
    

    这个应用程序需要访问daytime服务器,所以需要指定服务器。

    using boost::asio::ip::tcp;
    int main(int argc,char *argv[]) {
        try {
            if(argc != 2) {
                std::cerr << "Usage: client <host>" << std::endl;
                return 1;
            }
        }
    }
    

    使用命令行的目的地址,进行解析后再建立连接。

        boost::asio::io_context io_context;
        tcp::resolver resolver(io_context);
        tcp::resolver::results_type endpoints =
          resolver.resolve(argv[1], "daytime");
        tcp::socket socket(io_context);
    	//会自动遍历并尝试连接,直到成功
        boost::asio::connect(socket, endpoints);
    

    连接建立后,就可以从daytime服务器读取响应信息。

    我们使用boost::array来保存接收的数据。boost::asio::buffer() 函数自动确定数组的大小以帮助防止缓冲区溢出。我们可以使用 char [] std::vector 来代替 boost::array

        for (;;)
        {
          boost::array<char, 128> buf;
          boost::system::error_code error;
    
          size_t len = socket.read_some(boost::asio::buffer(buf), error);
    

    当服务器关闭连接时,ip::tcp::socket::read_some()函数将退出并出现 boost::asio::error::eof 错误,这就是退出循环的方式。

          if (error == boost::asio::error::eof)
            break; // Connection closed cleanly by peer.
          else if (error)
            throw boost::system::system_error(error); // Some other error.
    
          std::cout.write(buf.data(), len);
        }
    

    最后,处理抛出来的异常。

      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    

    完整代码如下:

    #include <iostream>
    #include <boost/array.hpp>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::tcp;
    
    int main(int argc, char* argv[])
    {
      try
      {
        if (argc != 2)
        {
          std::cerr << "Usage: client <host>" << std::endl;
          return 1;
        }
    
        boost::asio::io_context io_context;
    
        tcp::resolver resolver(io_context);
        tcp::resolver::results_type endpoints =
          resolver.resolve(argv[1], "daytime");
    
        tcp::socket socket(io_context);
        boost::asio::connect(socket, endpoints);
    
        for (;;)
        {
          boost::array<char, 128> buf;
          boost::system::error_code error;
    
          size_t len = socket.read_some(boost::asio::buffer(buf), error);
    
          if (error == boost::asio::error::eof)
            break; // Connection closed cleanly by peer.
          else if (error)
            throw boost::system::system_error(error); // Some other error.
    
          std::cout.write(buf.data(), len);
        }
      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    
      return 0;
    }
    

    同步的daytime服务端

    本节显示了如何使用asio实现一个TCP服务器应用。

    首先引入头文件

    #include <ctime>
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::tcp;
    

    我们定义了函数make_daytime_string()来生成返回给客户端的字符串。该函数可以被我们所有的daytime服务器应用重用。

    std::string make_daytime_string()
    {
      using namespace std; // For time_t, time and ctime;
      time_t now = time(0);
      return ctime(&now);
    }
    
    int main()
    {
      try
      {
        boost::asio::io_context io_context;
    

    创建一个acceptor对象来监听连接。接受连接后,将时间字符串发送到客户端。

        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13));
        for (;;)
        {
          tcp::socket socket(io_context);
          acceptor.accept(socket);
          //访问的时间
          std::string message = make_daytime_string();
    		//发送到客户端
          boost::system::error_code ignored_error;
          boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
        }
      }
    

    最后,处理异常

      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    
      return 0;
    }
    

    完整代码如下:

    #include <ctime>
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::tcp;
    
    std::string make_daytime_string()
    {
      using namespace std; // For time_t, time and ctime;
      time_t now = time(0);
      return ctime(&now);
    }
    
    int main()
    {
      try
      {
        boost::asio::io_context io_context;
    
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13));
    
        for (;;)
        {
          tcp::socket socket(io_context);
          acceptor.accept(socket);
    
          std::string message = make_daytime_string();
    
          boost::system::error_code ignored_error;
          boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
        }
      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    
      return 0;
    }
    

    异步的daytime服务端

    我们创建一个服务器对象来接受传入的客户端连接。

    在主函数中只需要启动服务器即可。

    int main() {
        try{
            boost::asio::io_context ioc;
            tcp_server server(ioc);
            ioc.run();
        } catch(std::exception& e) {
            std::cerr << e.what() << std::endl;
        }
        return 0;
    }
    

    关键在于tcp_server服务器类的实现,作为服务器,一定要有一个接收连接的接收器,然后还有对接收的连接的处理方法。

    构造函数在TCP的端口13初始化接收器。

    class tcp_server
    {
    public:
      tcp_server(boost::asio::io_context& io_context)
        : io_context_(io_context),
          acceptor_(io_context, tcp::endpoint(tcp::v4(), 13))
      {
        start_accept();
      }
    
    private:
    

    start_accept()创建一个套接字并使用一个异步accept操作等待一个新连接。

      void start_accept()
      {
        tcp_connection::pointer new_connection =
          tcp_connection::create(io_context_);
    
        acceptor_.async_accept(new_connection->socket(),
            boost::bind(&tcp_server::handle_accept, this, new_connection,
              boost::asio::placeholders::error));
      }
    

    当由 start_accept() 发起的异步接受操作完成时,将调用函数 handle_accept()。 它为客户端请求提供服务,然后调用 start_accept() 以启动下一个接受操作。

      void handle_accept(tcp_connection::pointer new_connection,
          const boost::system::error_code& error)
      {
        if (!error)
        {
          new_connection->start();
        }
    
        start_accept();
      }
    

    tcp_connection类

    我们将使用 shared_ptr enable_shared_from_this 因为我们想让 tcp_connection 对象保持活动状态,只要有操作引用它。

    class tcp_connection
      : public boost::enable_shared_from_this<tcp_connection>
    {
    public:
      typedef boost::shared_ptr<tcp_connection> pointer;
    
      static pointer create(boost::asio::io_context& io_context)
      {
        return pointer(new tcp_connection(io_context));
      }
    
      tcp::socket& socket()
      {
        return socket_;
      }
    

    tcp_connection对象就是我们用来封装对连接的操作的。

    在函数 start() 中,我们调用 boost::asio::async_write() 将数据提供给客户端。 请注意,我们使用的是 boost::asio::async_write(),而不是 ip::tcp::socket::async_write_some(),以确保发送整个数据块。

      void start()
      {
        message_ = make_daytime_string();
        boost::asio::async_write(socket_, boost::asio::buffer(message_),
            boost::bind(&tcp_connection::handle_write, shared_from_this(),
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred));   
    

    此客户端连接的任何进一步操作现在由 handle_write() 负责。

      }
    
    private:
      tcp_connection(boost::asio::io_context& io_context)
        : socket_(io_context)
      {
      }
    
      void handle_write(const boost::system::error_code& /*error*/,
          size_t /*bytes_transferred*/)
      {
      }
    
      tcp::socket socket_;
      std::string message_;
    };
    

    这里使用了智能指针,意在让TcpConnection对象执行完后自动释放,对于异步操作是有等待时间的,故为了让连接不会在等待时间内释放,那么我们就要将智能指针同样传递给回调参数。

    完整代码如下:

    #include <ctime>
    #include <iostream>
    #include <string>
    #include <boost/bind/bind.hpp>
    #include <boost/shared_ptr.hpp>
    #include <boost/enable_shared_from_this.hpp>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::tcp;
    
    std::string make_daytime_string()
    {
      using namespace std; // For time_t, time and ctime;
      time_t now = time(0);
      return ctime(&now);
    }
    
    class tcp_connection
      : public boost::enable_shared_from_this<tcp_connection>
    {
    public:
      typedef boost::shared_ptr<tcp_connection> pointer;
    
      static pointer create(boost::asio::io_context& io_context)
      {
        return pointer(new tcp_connection(io_context));
      }
    
      tcp::socket& socket()
      {
        return socket_;
      }
    
      void start()
      {
        message_ = make_daytime_string();
    
        boost::asio::async_write(socket_, boost::asio::buffer(message_),
            boost::bind(&tcp_connection::handle_write, shared_from_this(),
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred));
      }
    
    private:
      tcp_connection(boost::asio::io_context& io_context)
        : socket_(io_context)
      {
      }
    
      void handle_write(const boost::system::error_code& /*error*/,
          size_t /*bytes_transferred*/)
      {
      }
    
      tcp::socket socket_;
      std::string message_;
    };
    
    class tcp_server
    {
    public:
      tcp_server(boost::asio::io_context& io_context)
        : io_context_(io_context),
          acceptor_(io_context, tcp::endpoint(tcp::v4(), 13))
      {
        start_accept();
      }
    
    private:
      void start_accept()
      {
        tcp_connection::pointer new_connection =
          tcp_connection::create(io_context_);
    
        acceptor_.async_accept(new_connection->socket(),
            boost::bind(&tcp_server::handle_accept, this, new_connection,
              boost::asio::placeholders::error));
      }
    
      void handle_accept(tcp_connection::pointer new_connection,
          const boost::system::error_code& error)
      {
        if (!error)
        {
          new_connection->start();
        }
    
        start_accept();
      }
    
      boost::asio::io_context& io_context_;
      tcp::acceptor acceptor_;
    };
    
    int main()
    {
      try
      {
        boost::asio::io_context io_context;
        tcp_server server(io_context);
        io_context.run();
      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    
      return 0;
    }
    

    使用UDP实现

    同步的daytime客户端

    我们使用 ip::udp::resolver 对象根据主机名和服务名找到要使用的正确远程端点。 该查询被ip::udp::v4()参数限制为仅返回 IPv4 端点。

        udp::resolver resolver(io_context);
        udp::endpoint receiver_endpoint =
          *resolver.resolve(udp::v4(), argv[1], "daytime").begin();
    

    如果 ip::udp::resolver::resolve() 函数没有失败,它保证至少返回列表中的一个端点。 这意味着直接解引用的返回值是安全的。

    由于 UDP 是面向数据报的,因此我们不会使用流套接字。 创建一个 ip::udp::socket 并启动与远程端点的联系。

        udp::socket socket(io_context);
        socket.open(udp::v4());
    
        boost::array<char, 1> send_buf  = {{ 0 }};
        socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
    

    与TCP不同的是,我们UDP客户端必须要先发送数据过去,服务端才能收到客户端的信息。

    现在我们需要准备好接受服务器发回给我们的任何内容。 我们这边接收服务器响应的端点将由 ip::udp::socket::receive_from() 初始化。

        boost::array<char, 128> recv_buf;
        udp::endpoint sender_endpoint;
        size_t len = socket.receive_from(
            boost::asio::buffer(recv_buf), sender_endpoint);
    
        std::cout.write(recv_buf.data(), len);
      }
    

    完整代码如下:

    #include <iostream>
    #include <boost/array.hpp>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::udp;
    
    int main(int argc, char* argv[])
    {
      try
      {
        if (argc != 2)
        {
          std::cerr << "Usage: client <host>" << std::endl;
          return 1;
        }
    
        boost::asio::io_context io_context;
    
        udp::resolver resolver(io_context);
        udp::endpoint receiver_endpoint =
          *resolver.resolve(udp::v4(), argv[1], "daytime").begin();
    
        udp::socket socket(io_context);
        socket.open(udp::v4());
    
        boost::array<char, 1> send_buf  = {{ 0 }};
        socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
    
        boost::array<char, 128> recv_buf;
        udp::endpoint sender_endpoint;
        size_t len = socket.receive_from(
            boost::asio::buffer(recv_buf), sender_endpoint);
    
        std::cout.write(recv_buf.data(), len);
      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    
      return 0;
    }
    

    同步的daytime服务器

    创建一个UDP的套接字对象用来接收UDP端口13上的请求。

    每接收到一个数据报就意味着客户端的一次请求,我们要将响应发送过去。

    完整代码如下:

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/array.hpp>
    
    using boost::asio::ip::udp;
    
    int main(int argc, char* argv[]) {
      try {
        if(argc != 2) {
          std::cerr << "Usage client <host> " << std::endl;
          return 1;
        }
        boost::asio::io_context ioc;
        udp::resolver resolver(ioc);
        //解析出端点,我们只使用一个ipv4的
        udp::endpoint endpoint = *resolver.resolve(udp::v4(),argv[1],"daytime").begin();
    
        //创建UDP套接字
        udp::socket socket(ioc);
        socket.open(udp::v4());
    
        //向服务端发送数据,然后接受响应并打印出来
        boost::array<char,1> send_buf = {0};
        socket.send_to(boost::asio::buffer(send_buf),endpoint);
    
        boost::array<char,128> recv_buf;
        udp::endpoint send_endpoint;
        size_t len = socket.receive_from(boost::asio::buffer(recv_buf),send_endpoint);
    
        std::cout.write(recv_buf.data(),len);
    
      } catch(std::exception& e) {
        std::cerr << e.what() << std::endl;
      }
    } 
    

    异步的daytime服务器

    通过一个服务器对象来接收客户端请求

    int main() {
      try {
        boost::asio::io_context ioc;
        UdpServer server(ioc);
    
        ioc.run();
      } catch(const std::exception& e) {
        std::cerr << e.what() << '
    ';
      }
      return 0;
    }
    

    服务器中启动接收数据

    void start_receive() {
        boost::array<char,1> recv_buf;
        socket_.async_receive_from(boost::asio::buffer(recv_buf),
                                    remote_endpoit_,
                                    boost::bind(&UdpServer::handle_receive,
                                                this,
                                                boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred));
      }
    

    接收完毕后,开始向对端发送响应,处理结束后等待下一个请求。

    void handle_receive(const boost::system::error_code& error,std::size_t len) {
        std::string message = make_daytime_string();
        socket_.async_send_to(boost::asio::buffer(message),
                              remote_endpoit_,
                              boost::bind(&UdpServer::handle_send,
                                          this,
                                          boost::asio::placeholders::error,
                                          boost::asio::placeholders::bytes_transferred));
        start_receive();
      }
    

    完整代码如下:

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/array.hpp>
    #include <boost/bind.hpp>
    #include <ctime>
    #include <string>
    
    using boost::asio::ip::udp;
    
    std::string make_daytime_string() {
      time_t now = time(0);
      return ctime(&now);
    }
    
    class UdpServer {
     public:
      UdpServer(boost::asio::io_context& ioc) : socket_(ioc,udp::endpoint(udp::v4(),13)) {
        start_receive();
      }
     private:
      void start_receive() {
        boost::array<char,1> recv_buf;
        socket_.async_receive_from(boost::asio::buffer(recv_buf),
                                    remote_endpoit_,
                                    boost::bind(&UdpServer::handle_receive,
                                                this,
                                                boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred));
      }
      void handle_receive(const boost::system::error_code& error,std::size_t len) {
        std::string message = make_daytime_string();
        socket_.async_send_to(boost::asio::buffer(message),
                              remote_endpoit_,
                              boost::bind(&UdpServer::handle_send,
                                          this,
                                          boost::asio::placeholders::error,
                                          boost::asio::placeholders::bytes_transferred));
        start_receive();
      }
      void handle_send(const boost::system::error_code& ,std::size_t len) {
    
      }
      udp::socket socket_;
      udp::endpoint remote_endpoit_;
    };
    
    int main() {
      try {
        boost::asio::io_context ioc;
        UdpServer server(ioc);
    
        ioc.run();
      } catch(const std::exception& e) {
        std::cerr << e.what() << '
    ';
      }
      return 0;
    }
    

    TCP/UDP结合的异步服务器

    我们将刚才的两个TCP和UDP异步服务器结合到一个服务器应用中。

    其实就是在主函数中启动两个服务器而已,其他代码都不需要更改,直接就可以使用了。

    int main()
    {
      try
      {
        boost::asio::io_context io_context;
        tcp_server server1(io_context);
        udp_server server2(io_context);
        io_context.run();
      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }
    
      return 0;
    }
    
  • 相关阅读:
    Linux启动ftp服务器530 Permission denied解决方法
    Cloudera的CDH和Apache的Hadoop的区别
    我的vm虚拟机网络设置
    本地Yum软件源安装Cloudera Manager 5
    SSH无法登陆服务器,但是可以ping通,解决方法
    Linux (CentOS)增加删除用户
    SSH创建公钥实现无密码操作失败原因
    chkconfig命令详解
    camon详细解决过程
    @修饰器
  • 原文地址:https://www.cnblogs.com/ylcc-zyq/p/14962842.html
Copyright © 2011-2022 走看看