1. boost::asio介紹:
(1)Boost.Asio是一個跨平台的、主要用於網絡和其他一些底層輸入/輸出編程的C++庫。Boost.Asio在網絡通信抽象了IO概念,可以用它進行同步或者異步的IO網絡編程。Boost.Asio可以在大多數操作系統上使用,能同時支持數千個並發的連接。
(2)Asio可以進行異步網絡編程,其采用前攝器模式實現異步IO,不需要多線程和鎖機制(避免了競爭和死鎖)。它內部封裝了select、kqueue、poll/epoll、overlappedIO等機制。
(3)io_service是asio庫的異步處理處理機制(類似epoll),它負責與操作系統交互,通過調用其run()函數來等待所有的異步操作完成,並為每一個異步操作調用其handler回調函數。它必須先初始化,
(4)Asio工作在同步模式:程序發起IO操作請求,隨即向IO_service提交請求,由IO_service將請求轉交給內核,之后阻塞等待IO操作完成。IO操作完成后由內核通知IO_service,IO_service將結果交給程序。
Asio工作在異步模式:程序發起IO操作請求,隨即向IO_service提交請求,由IO_service將請求轉發給內核,同時注冊handler回調函數,之后立即返回。IO_service的run函數將等待IO操作完成,完成后從內核取回結果並調用handler函數。
2. boost::asio有關網絡通信的API:
(1)IP地址類:
- p::address(v4_or_v6_address):把一個ipv4或者ipv6地址轉換成ip::address
- ip::address::from_string(str):根據一個IPv4地址(.隔開)或者一個IPv6地址(十六進制)創建一個地址。
- ip::address::to_string() :返回這個地址的字符串。
(2)端點類:
- endpoint(protocol, port):創建可以接受新連接的Server端點。
- endpoint(addr, port):創建一個連接到某個地址和端口的Client端點。
(3)套接字類:
socket類可以創建一個相應的socket,而且總是在構造的時候傳入io_service實例:
io_service ios;
ip::tcp::socket sock(ios);
(4)socket類的常用方法:
TCP連接相關
- open(protocol):用給定的IP協議(v4或者v6)打開一個socket。主要用在UDP客戶端 或者 服務端socket
- bind(endpoint):將套接字綁定到一個地址和端口
- connect(endpoint):Client用同步的方式連接到Server
- async_connect(endpoint, handler):Client用異步的方式連接到Server,連接成功后,調用handler回調函數。
- close():這個函數用來關閉套接字。
TCP讀寫相關
- async_read_some(buffer,handler):從套接字異步接收數據。接收完成后,調用handler回調函數。
- async_write_some(buffer, handler):異步發送緩沖區數據到套接字。發送完成后,調用handler回調函數。
- read_some(buffer):同步地從所給的緩沖區讀取數據。在讀完所有數據或者錯誤出現之前,這個函數都是阻塞的。
- write_some(buffer):同步地發送緩沖區的數據。在所有數據發送成功或者出現錯誤之前,這個函數都是阻塞的。
其他方法
- local_endpoint():這個方法返回套接字本地連接的地址。
- remote_endpoint():這個方法返回套接字連接到的遠程地址。
- non_blocking():如果套接字是非阻塞的,這個方法返回true,否則false。
( ps:為了使socket和緩沖區(read或者write)在整個異步操作的生命周期中一直活動,需要采取特殊的防護措施。連接類需要繼承自enabled_shared_from_this,然后在內部保存它需要的緩沖區,而且每次異步調用都要傳遞一個智能指針給this操作。)
3. 使用boost::asio實現TCP客戶端和服務器端:
(1)Server端:
#include <iostream> #include <boost/shared_ptr.hpp> #include <boost/asio.hpp> #include <boost/asio/placeholders.hpp> #include <boost/system/error_code.hpp> #include <boost/bind/bind.hpp> #include "stdafx.h" using namespace boost::asio; using namespace std; typedef boost::shared_ptr<ip::tcp::socket> sock_ptr; class server { private: io_service m_io; ip::tcp::acceptor m_acceptor; public: server() : m_acceptor(m_io, ip::tcp::endpoint(ip::tcp::v4(), 6688)) { accept(); } void run(){ m_io.run();} void accept() { sock_ptr sock(new ip::tcp::socket(m_io)); m_acceptor.async_accept(*sock, boost::bind(&server::accept_handler, this, boost::asio::placeholders::error, sock)); } void accept_handler(const boost::system::error_code& ec, sock_ptr sock) { if (ec) { return; } cout<<"Client:"; cout<<sock->remote_endpoint().address()<<endl; sock->async_write_some(buffer("hello asio"), boost::bind(&server::write_handler, this, boost::asio::placeholders::error)); // 發送完畢后繼續監聽,否則io_service將認為沒有事件處理而結束運行 accept(); } void write_handler(const boost::system::error_code&ec) { cout<<"send msg complete"<<endl; } }; int main() { try { cout<<"Server start."<<endl; server srv; srv.run(); } catch (std::exception &e) { cout<<e.what()<<endl; } return 0; }
(2)Client端:
#include <iostream> #include <boost/shared_ptr.hpp> #include <boost/asio.hpp> #include <boost/asio/placeholders.hpp> #include <boost/system/error_code.hpp> #include <boost/bind/bind.hpp> #include "stdafx.h" using namespace boost::asio; using namespace std; typedef boost::shared_ptr<ip::tcp::socket> sock_ptr; typedef vector<char> buffer_type; class client { private: io_service m_io; buffer_type m_buf; ip::tcp::endpoint m_ep; public: client(): m_buf(100, 0), m_ep(ip::address::from_string("127.0.0.1"), 6688) { start(); } void run() { m_io.run();} void start() { sock_ptr sock(new ip::tcp::socket(m_io)); sock->async_connect(m_ep, boost::bind(&client::conn_handler, this, boost::asio::placeholders::error, sock)); } void conn_handler(const boost::system::error_code&ec, ip::tcp::socket sock) { if (ec) {return;} cout<<"Receive from "<<sock->remote_endpoint().address()<<": "<<endl; sock->async_read_some(buffer(m_buf), boost::bind(&client::read_handler, this, boost::asio::placeholders::error, sock)); } void read_handler(const boost::system::error_code&ec, ip::tcp::socket sock) { if (ec) {return;} sock->async_read_some(buffer(m_buf), boost::bind(&client::read_handler, this, boost::asio::placeholders::error, sock)); cout<<&m_buf[0]<<endl; } }; int main() { try { cout<<"Client start."<<endl; client cli; cli.run(); } catch (std::exception &e) { cout<<e.what()<<endl; } return 0; }