Boost.Asio的网络编程

简介

这篇笔记是boost::asio的概览, 主要说明了boost的进行CS结构编程的基本步骤. 在网络编程中, 又很多需要IO的操作. 一种是使用Linux的原生C语言API, Linux的核心编程思想是操作文件描述符, 所有的操作都是基于文件描述符. 而在boost中, 增添了更多功能的操作, 而且更加增强了关于异步的操作. 这篇笔记主要记录了使用boost::asio库进行的操作.

客户端

客户端主要的操作步骤是:

  • 建立io_service, 这是程序和内核的IO连接, 需要使用boost::asio::io_context来建立. 本例子中, 使用:
    boost::asio::io_context io_context;
    
  • 需要一个解析器解析服务器的信息, 比如IP和端口, boost::asio::ip::tcp::resolver进行解析, 在本例子中:
    tcp::resolver resolver(io_context);
    
  • 需要一个终端抽象, 来存储解析器解析的信息, 在本例子中:
    boost::asio::ip::tcp::resolver::results_type endpoints =
      resolver.resolve(argv[1], "6768");  // ip和6768端口
    
  • 之后, 需要一个socket建立连接, 使用:
    boost::asio::ip::tcp::socket socket(io_context);
    
  • 建立与服务器的链接, 是socketendpoints的连接:
    boost::asio::connect(socket, endpoints);
    
  • 最后是通过有关的读写函数进行操作, 具体参考代码

代码实例, 参考自: https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/tutorial/tutdaytime1/src.html

#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], "6768");
        // 解析存储的信息, 建议使用auto自动推导, 上下两个等价
        auto endpoints = resolver.resolve(argv[1], "6768");  

        tcp::socket socket(io_context);  // 与上下文相关的套接字
        boost::asio::connect(socket, endpoints);  // 建立连接

        for (;;) {
            boost::array<char, 128> buf;
            boost::system::error_code error;
				
		    // 数据的缓冲区, 参考: https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/buffer.html
            size_t len = socket.read_some(boost::asio::buffer(buf), error);

            if (error == boost::asio::error::eof) {
                std::cout << "closed by server\n";
                break; // 服务器主动关闭连接.
            }
            else if (error) {
                throw boost::system::system_error(error); // 其他的错误.
                std::cout << "error: ";
            }

            std::cout.write(buf.data(), len);  // 控制台输出
        }
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

同步服务器

服务编程的主要步骤是:

  • 建立上下文与内核进行连接, 与客户端一样:
    boost::asio::io_context io_context;
    
  • 需要有一个接收器, 用于在指定端口接收数据, 并通过指定的套接字进行读写:
    using boost::asio::ip::tcp;
    // ipv4地址, 并监听6768端口
    tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 6768));
    
  • 建立套接字, 用于数据传输
    tcp::socket socket(io_context);
    acceptor.accept(socket);
    
  • 通过有关函数, 写入或者读取数据

代码实例, 参考: https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/tutorial/tutdaytime2/src.html

#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

std::string make_daytime_string() {
    using namespace std;
    time_t now = time(0);
    return ctime(&now);
}

int main() {
    try {
        boost::asio::io_context io_context;
	    // 接收器, 初始化指定上下文 ip和端口
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 6768));

        for (;;) {
            tcp::socket socket(io_context);  // 建立socket
            acceptor.accept(socket);  // 用于读写数据的socket

            std::string message = make_daytime_string();

            boost::system::error_code ignored_error;
            // 向socket写入数据
            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/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:
    using pointer = boost::shared_ptr<tcp_connection>;

    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:
    explicit tcp_connection(boost::asio::io_context &io_context)
            : socket_(io_context) {
    }

    void handle_write(const boost::system::error_code &, size_t) {
		// 该函数一般是用户自己实现, 在异步写完后, 会调用该函数
    }

    tcp::socket socket_;
    std::string message_;
};

class tcp_server {
public:
    explicit tcp_server(boost::asio::io_context &io_context)
            : acceptor_(io_context, tcp::endpoint(tcp::v4(), 6768)) {
        start_accept();
    }

private:
    void start_accept() {
        auto new_connection =
                tcp_connection::create(acceptor_.get_executor().context());
        // 该函数调用后, 立刻返回, 建立连接完成后, 会调用handle_accept函数
        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();
    }

    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;
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值