zoukankan      html  css  js  c++  java
  • boost::asio::ip::tcp实现网络通信的小例子(转载) fengyc 博客园

    boost::asio::ip::tcp实现网络通信的小例子(转载) - fengyc - 博客园

    原文地址:http://www.cppblog.com/janvy/archive/2010/03/24/110478.html

    服务端:

    Boost.Asio是一个跨平台的网络及底层IO的C++编程库,它使用现代C++手法实现了统一的异步调用模型。

    头文件

    #include <boost/asio.hpp>             

    名空间

    using namespace boost::asio;             

    ASIO库能够使用TCP、UDP、ICMP、串口来发送/接收数据,下面先介绍TCP协议的读写操作

    对于读写方式,ASIO支持同步和异步两种方式,首先登场的是同步方式,下面请同步方式自我介绍一下:

    大家好!我是同步方式!

    我的主要特点就是执着!所有的操作都要完成或出错才会返回,不过偶的执着被大家称之为阻塞,实在是郁闷~~(场下一片嘘声),其实这样 也是有好处的,比如逻辑清晰,编程比较容易。

    在服务器端,我会做个socket交给acceptor对象,让它一直等客户端连进来,连上以后再通过这个socket与客户端通信, 而所有的通信都是以阻塞方式进行的,读完或写完才会返回。

    在客户端也一样,这时我会拿着socket去连接服务器,当然也是连上或出错了才返回,最后也是以阻塞的方式和服务器通信。

    有人认为同步方式没有异步方式高效,其实这是片面的理解。在单线程的情况下可能确实如此,我不能利用耗时的网络操作这段时间做别的事 情,不是好的统筹方法。不过这个问题可以通过多线程来避免,比如在服务器端让其中一个线程负责等待客户端连接,连接进来后把socket交给另外的线程去 和客户端通信,这样与一个客户端通信的同时也能接受其它客户端的连接,主线程也完全被解放了出来。

    我的介绍就有这里,谢谢大家!

    好,感谢同步方式的自我介绍,现在放出同步方式的演示代码(起立鼓掌!):

    服务器端

    1. #include <iostream>
    2. #include <boost/asio.hpp>
    3.  
    4. using namespace boost::asio;
    5.  
    6. int main(int argc, char* argv[])
    7. {
    8.     // 所有asio类都需要io_service对象
    9.     io_service iosev;
    10.     ip::tcp::acceptor acceptor(iosev, 
    11.         ip::tcp::endpoint(ip::tcp::v4(), 1000));
    12.     for(;;)
    13.     {
    14.         // socket对象
    15.         ip::tcp::socket socket(iosev);
    16.         // 等待直到客户端连接进来
    17.         acceptor.accept(socket);
    18.         // 显示连接进来的客户端
    19.         std::cout << socket.remote_endpoint().address() << std::endl;
    20.         // 向客户端发送hello world!
    21.         boost::system::error_code ec;
    22.         socket.write_some(buffer("hello world!"), ec);
    23.  
    24.         // 如果出错,打印出错信息
    25.         if(ec)
    26.         {
    27.             std::cout << 
    28.                 boost::system::system_error(ec).what() << std::endl;
    29.             break;
    30.         }
    31.         // 与当前客户交互完成后循环继续等待下一客户连接
    32.     }
    33.     return 0;

    客户端

    1. #include <iostream>
    2. #include <boost/asio.hpp>
    3.  
    4. using namespace boost::asio;
    5.  
    6. int main(int argc, char* argv[])
    7. {
    8.     // 所有asio类都需要io_service对象
    9.     io_service iosev;
    10.     // socket对象
    11.     ip::tcp::socket socket(iosev);
    12.     // 连接端点,这里使用了本机连接,可以修改IP地址测试远程连接
    13.     ip::tcp::endpoint ep(ip::address_v4::from_string("127.0.0.1"), 1000);
    14.     // 连接服务器
    15.     boost::system::error_code ec;
    16.     socket.connect(ep,ec);
    17.     // 如果出错,打印出错信息
    18.     if(ec)
    19.     {
    20.         std::cout << boost::system::system_error(ec).what() << std::endl;
    21.         return -1;
    22.     }
    23.     // 接收数据
    24.     char buf[100];
    25.     size_t len=socket.read_some(buffer(buf), ec);
    26.     std::cout.write(buf, len);
    27.  
    28.     return 0;

    从演示代码可以得知

    • ASIO的TCP协议通过boost::asio::ip名 空间下的tcp类进行通信。
    • IP地址(address,address_v4,address_v6)、 端口号和协议版本组成一个端点tcp:: endpoint)。用于在服务器端生成tcp::acceptor对 象,并在指定端口上等待连接;或者在客户端连接到指定地址的服务器上。
    • socket是 服务器与客户端通信的桥梁,连接成功后所有的读写都是通过socket对 象实现的,当socket析 构后,连接自动断 开。
    • ASIO读写所用的缓冲区用buffer函 数生成,这个函数生成的是一个ASIO内部使用的缓冲区类,它能把数组、指针(同时指定大 小)、std::vector、std::string、boost::array包装成缓冲区类。
    • ASIO中的函数、类方法都接受一个boost::system::error_code类 型的数据,用于提供出错码。它可以转换成bool测试是否出错,并通过boost::system::system_error类 获得详细的出错信息。另外,也可以不向ASIO的函数或方法提供 boost::system::error_code,这时如果出错的话就会直 接抛出异常,异常类型就是boost::system:: system_error(它是从std::runtime_error继承的)。
    • 另一个例子::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    • 我稍稍整理了下,就是加了点注释,很基本的东西,大家可以参考socket的几个流程,我上面也有提示的,希望对大家有所帮助。最后,如果大家有什么好的方法希望能让我也分享下,谢谢!

    • #
      include <boost/asio.hpp>
      #include <boost/bind.hpp>
      #include <boost/shared_ptr.hpp>
      #include <boost/enable_shared_from_this.hpp>
      #include <iostream>
      using boost::asio::ip::tcp;
      #define max_len 1024

      class clientSession
          :public boost::enable_shared_from_this<clientSession>
      {
      public:
          clientSession(boost::asio::io_service& ioservice)
              :m_socket(ioservice)
          {
              memset(data_,'\0',sizeof(data_));
          }
          ~clientSession()
          {}
          tcp::socket& socket()
          {
              return m_socket;
          }
          void start()
          {
              boost::asio::async_write(m_socket,
                  boost::asio::buffer("link successed!"),
                  boost::bind(&clientSession::handle_write,shared_from_this(),
                  boost::asio::placeholders::error));

              /*async_read跟客户端一样,还是不能进入handle_read函数,如果你能找到问题所在,请告诉我,谢谢*/

    // --已经解决,boost::asio::async_read(...)读取的字节长度不能大于数据流的长度,否则就会进入

    // ioservice.run()线程等待,read后面的就不执行了。
            //boost::asio::async_read(m_socket,boost::asio::buffer(data_,max_len),

            //        boost::bind(&clientSession::handle_read,shared_from_this(),

            //        boost::asio::placeholders::error));


            //max_len可以换成较小的数字,就会发现async_read_some可以连续接收未收完的数据

            m_socket.async_read_some(boost::asio::buffer(data_,max_len),
                    boost::bind(&clientSession::handle_read,shared_from_this(),
                    boost::asio::placeholders::error));
        }
    private:
        void handle_write(const boost::system::error_code& error)
        {
            
            if(error)
            {
                m_socket.close();
            }
            
        }
        void handle_read(const boost::system::error_code& error)
        {
            
            if(!error)
            {
                std::cout << data_ << std::endl;
                //boost::asio::async_read(m_socket,boost::asio::buffer(data_,max_len),

                //    boost::bind(&clientSession::handle_read,shared_from_this(),

                //    boost::asio::placeholders::error));

                m_socket.async_read_some(boost::asio::buffer(data_,max_len),
                    boost::bind(&clientSession::handle_read,shared_from_this(),
                    boost::asio::placeholders::error));
            }
            else
            {
                m_socket.close();
            }
        
        }
    private:
        tcp::socket m_socket;
        char data_[max_len];
    };


    class serverApp
    {
        typedef boost::shared_ptr<clientSession> session_ptr;
    public:
        serverApp(boost::asio::io_service& ioservice,tcp::endpoint& endpoint)
            :m_ioservice(ioservice),
            acceptor_(ioservice,endpoint)
        {
            session_ptr new_session(new clientSession(ioservice));
            acceptor_.async_accept(new_session->socket(),
                boost::bind(&serverApp::handle_accept,this,boost::asio::placeholders::error,
                new_session));
        }
        ~serverApp()
        {
        }
    private:
        void handle_accept(const boost::system::error_code& error,session_ptr& session)
        {
            if(!error)
            {
                std::cout << "get a new client!" << std::endl;
                //实现对每个客户端的数据处理

                session->start();
                //在这就应该看出为什么要封session类了吧,每一个session就是一个客户端

                session_ptr new_session(new clientSession(m_ioservice));
                acceptor_.async_accept(new_session->socket(),
                boost::bind(&serverApp::handle_accept,this,boost::asio::placeholders::error,
                new_session));
            }
        }
    private:
        boost::asio::io_service& m_ioservice;
        tcp::acceptor acceptor_;
    };

    int main(int argc , char* argv[])
    {
        boost::asio::io_service myIoService;
        short port = 8100/*argv[1]*/;
        //我们用的是inet4

        tcp::endpoint endPoint(tcp::v4(),port);
        //终端(可以看作sockaddr_in)完成后,就要accept了

        serverApp sa(myIoService,endPoint);
        //数据收发逻辑

        myIoService.run();
        return 0;
    }

  • 相关阅读:
    NTP on FreeBSD 12.1
    Set proxy server on FreeBSD 12.1
    win32 disk imager使用后u盘容量恢复
    How to install Google Chrome Browser on Kali Linux
    Set NTP Service and timezone on Kali Linux
    Set static IP address and DNS on FreeBSD
    github博客标题显示不了可能是标题包含 特殊符号比如 : (冒号)
    server certificate verification failed. CAfile: none CRLfile: none
    删除文件和目录(彻底的)
    如何在Curl中使用Socks5代理
  • 原文地址:https://www.cnblogs.com/lexus/p/2703943.html
Copyright © 2011-2022 走看看