boost--asio
1、asio綜述
asio的核心類是io_service,它相當於前攝器模式的Proactor角色,在異步模式下發起的I/O操作,需要定義一個用於回調的完成處理函數,當I/O完成時io_service會調用註冊的完成處理函數。通過調用io_service的成員函數run()來啟動前攝器的事件處理循環,阻塞等待所有的操作完成並分派事件。在異步模式下,如果不調用run()就沒有了等待異步操作完成的機制,回調函數將得不到執行。
asio不直接使用線程,而是定義了一個自己的線程概念:strand,它保證在多線程環境中不使用互斥量代碼可以正確執行,io_service::strand::wrap()可以封裝一個函數在strand中執行。
asio中的兩個類mutable_buffer和const_buffer用來封裝緩沖區,可以被安全的用在異步讀寫中。asio通常不能直接使用數組、string、vector等來作為收發數據的緩沖區,函數buffer()能夠包裝常用的數據類型(數組、string、vector等)用於asio的數據收發。
asio的函數有兩種重載形式:一種是有一個error_code(boost::system庫中)的輸出參數,調用函數後可以檢查這個參數以判斷是否發生了錯誤;一種是沒有error_code參數,但發生了錯誤會拋出system_error(boost::system庫中)異常,調用函數的時候應該使用try-catch塊來捕獲錯誤。
使用asio需要包含頭文件"boost\asio.hpp",在VC下還要添加以下定義避免編譯警告:
#ifdef _MSC_VER #define _WIN32_WINNT 0X0501 #endif #include "boost\asio.hpp"
2、定時器
deadline_timer是asio中的定時器類,它有兩種形式的構造函數,第一個參數都是一個io_service對象,第二個參數可以是posix_time的絕對時間或者是自當前時間開始的一個時間段。創建定時器並指定終止時間後它就會立即開始計時,如果創建定時器時不指定終止時間,那麽定時器不會開始工作,可以使用成員函數expires_at()或expires_from_now()設置終止時間,調用wait()同步等待或async_wai()設置異步等待,並註冊完成時的回調handler。
deadline_timer的成員函數expires_at()可以獲得計時器終止的絕對時間,成員函數cancel()用來取消異步操作。
deadline_timer的一些功能需要包含頭文件"boost\date_time\posix_time\posix_time.hpp"。
下面為一個同步定時器的使用:
#ifdef _MSC_VER #define _WIN32_WINNT 0X0501 #endif #include "boost\asio.hpp" #include "boost\date_time\posix_time\posix_time.hpp" int main() { boost::asio::io_service ios; boost::asio::deadline_timer dt(ios, boost::posix_time::seconds(3)); cout << dt.expires_at() << endl; //輸出終止時間 dt.wait(); //一直等待定時器終止 cout << "here" << endl; return 0; }View Code
以下是一個異步定時器的使用:
#ifdef _MSC_VER #define _WIN32_WINNT 0X0501 #endif #include "boost\asio.hpp" #include "boost\date_time\posix_time\posix_time.hpp" void Printf(const boost::system::error_code& e, int n) { cout << "Printf func, thread id: " << n << endl; cout << boost::this_thread::get_id() << endl; } int main() { cout << "main thread id: " << boost::this_thread::get_id() << endl; boost::asio::io_service ios; boost::asio::deadline_timer dt(ios, boost::posix_time::seconds(3)); int num = 999; dt.async_wait(bind(Printf, _1, num)); //通知io_service異步的執行I/O操作,並註冊回調函數後立即返回 ios.run(); //啟動Proactor的事件處理循環 cout << "here" << endl; return 0; }View Code
3、同步socket通信
asio主要支持TCP、UDP、ICMP協議,在名字空間boost::asio::ip裏提供了大量的網絡通信的功能類和函數。ip::address是IP地址類,在ip::tcp類裏包含了網絡通信主要的類,如:端點類endpoint、套接字類socket、流類iostream、接受器acceptor、解析器resolver等。
同步socket處理客戶端:
#include <iostream>
#include <vector>
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
using namespace std;
void main()
try
{
cout << "client start." << endl;
boost::asio::io_service ios; //asio程序必需的io_service對象
boost::asio::ip::tcp::socket sock(ios); //創建socket對象
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 6688); //創建連接點
sock.connect(endpoint); //連接到端點
vector<char> str(100, 0);
sock.read_some(boost::asio::buffer(str)); //接收數據,使用buffer()包裝緩沖區
cout << "recive from " << sock.remote_endpoint().address().to_string() << ": " << &str[0] << endl; //輸出對方地址及發送內容
//sock.close(); //sock析構的時候會自動調用close()
getchar();
}
catch (exception& e)
{
cout << e.what() << endl;
}
View Code
同步socket處理服務端:
#include <iostream>
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
using namespace std;
typedef boost::asio::io_service io_service_boost;
typedef boost::asio::ip::tcp::acceptor acceptor_boost;
typedef boost::asio::ip::tcp::endpoint endpoint_boost;
typedef boost::asio::ip::tcp::socket socket_boost;
void main()
try
{
cout << "server start, bind ";
io_service_boost ios; //asio程序必需的io_service對象
acceptor_boost acceptor(ios, endpoint_boost(boost::asio::ip::tcp::v4(), 6688)); //創建acceptor對象,綁定本機IP及6688端口
cout << acceptor.local_endpoint().address() << ": " << acceptor.local_endpoint().port() << endl; //輸出綁定的地址信息
while (true)
{
socket_boost sock(ios); //創建socket對象
acceptor.accept(sock); //等待客戶連接
cout << sock.remote_endpoint().address() << ": " << sock.remote_endpoint().port() << " now connect." << endl; //輸出對方信息
sock.write_some(boost::asio::buffer("hello!")); //發送數據,使用buffer()包裝緩沖區
//sock.close(); //sock析構的時候會自動調用close()
}
}
catch(exception& e)
{
cout << e.what() << endl;
}
View Code
4、異步socket通信
異步socket通信的函數名稱比同步的函數多了async_,且應該設置異步操作完成後的回調函數,以下為使用異步socket通信的服務端示例:
#include <iostream>
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\smart_ptr.hpp"
#include "boost/bind.hpp"
#include "boost\asio.hpp"
using namespace std;
typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::tcp::acceptor acceptor_t;
typedef boost::asio::ip::tcp::endpoint endpoint_t;
typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::asio::ip::tcp tcp_t;
typedef boost::shared_ptr<socket_t> spSock_t;
class CServer
{
public:
CServer(io_service_t& io)
: ios(io) //使用傳入的io_service
, acceptor(ios, endpoint_t(tcp_t::v4(), 6688)) //綁定本機IP及6688端口
{
accept_async();
}
virtual ~CServer(){}
public:
void accept_async() //發起一個異步接受連接
{
spSock_t spSock(new socket_t(ios));
acceptor.async_accept(*spSock, boost::bind(&CServer::accept_handler, this, _1, spSock));
}
void accept_handler(const boost::system::error_code& ec, spSock_t spSock) //accept完成後的回調
{
if (ec) //檢測錯誤碼
return;
cout << "a client connect, form ";
cout << spSock->remote_endpoint().address() << ": " << spSock->remote_endpoint().port() << endl; //輸出客戶地址和端口號
spSock->async_write_some(boost::asio::buffer("hello"), boost::bind(&CServer::send_handler, this, _1, spSock)); //發送數據
accept_async(); //再次發起一個異步接受連接
}
void send_handler(const boost::system::error_code& ec, spSock_t spSock) //發送數據完成的回調
{
cout << "send msg complete" << endl;
}
private:
io_service_t& ios;
acceptor_t acceptor;
};
void main()
{
cout << "server start." << endl;
io_service_t ios;
CServer srv(ios);
ios.run(); //開啟事件處理循環
}
View Code
5、解析器resolver
resolver的主要功能是通過主機名獲得主機地址等信息,相當於getaddrinfo()。以下封裝了一個使用域名來連接服務器的函數resolv_connect():
#include <iostream>
using namespace std;
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\lexical_cast.hpp"
typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::asio::ip::tcp::resolver resolver_t;
typedef boost::system::error_code error_code_t;
void resolv_connect(socket_t& sock, const char*name, int port)
{
resolver_t rlv(sock.get_io_service());
resolver_t::query qry(name, boost::lexical_cast<string>(port));
resolver_t::iterator iter = rlv.resolve(qry);
resolver_t::iterator end; //逾尾叠代器
error_code_t ec = boost::asio::error::host_not_found;
for (; ec && iter != end; ++iter) //開始叠代端點
{
sock.close();
sock.connect(*iter, ec);
}
if (ec)
{
cout << "can not connect." << endl;
throw boost::system::system_error(ec);
}
else
{
cout << "connect sucess." << endl;
}
}
int main()
{
io_service_t ios;
socket_t sock(ios);
resolv_connect(sock, "www.baidu.com", 80);
sock.close();
return 0;
}
View Code
resolver不僅能夠解析域名,也支持使用IP地址和服務名,如:
boost::asio::ip::tcp::resolver::query qry("127.0.0.1", "http");
6、超時處理
如果要實現超時處理,應該在異步調用後聲明一個deadline_timer對象,然後設置deadline_timer的等待時間和回調函數就可以了。如下代碼示例了異步連接服務器,如果超過5秒還沒有完成則關閉socket:
#include <iostream>
using namespace std;
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\lexical_cast.hpp"
#include "boost\bind.hpp"
typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::asio::ip::tcp::resolver resolver_t;
typedef boost::system::error_code error_code_t;
typedef boost::asio::ip::tcp::endpoint endpoint_t;
typedef boost::asio::ip::address address_t;
void connect_timeout(const error_code_t&, socket_t* sock)
{
sock->close();
}
int main()
{
io_service_t ios;
socket_t sock(ios);
endpoint_t ep(address_t::from_string("127.0.0.1"), 6688);
sock.async_connect(ep, connect_handler);
boost::asio::deadline_timer t(ios, boost::posix_time::seconds(5)); //5秒等待時間
t.async_wait(boost::bind(connect_timeout, _1, &sock)); //超時回調
... //其它操作
ios.run();
return 0;
}
View Code
7、流操作
對於TCP連接,可以使用asio::ip::tcp::iostream來代替socket,它也是std::basic_iostream的子類,可以像標準流一樣來操作socket,它內部集成了connect和resolver域名解析功能,使用示例:
#include <iostream> using namespace std; #ifdef _MSC_VER #define _WIN32_WINNT 0X0501 #endif #include "boost\asio.hpp" typedef boost::asio::io_service io_service_t; typedef boost::asio::ip::tcp::socket socket_t; typedef boost::asio::ip::tcp::resolver resolver_t; typedef boost::system::error_code error_code_t; typedef boost::asio::ip::tcp::endpoint endpoint_t; typedef boost::asio::ip::address address_t; typedef boost::asio::ip::tcp::acceptor acceptor_t; typedef boost::asio::ip::tcp tcp_t; //客戶端 { ... boost::asio::ip::tcp::iostream tcp_stream("127.0.0.1", 6688); //連接到本機6688端口 string str; getline(tcp_stream, str); //從數據流中讀取一行數據 cout << str << endl; ... } //服務端 { ... io_service_t ios; endpoint_t ep(tcp_t::v4(), 6688); acceptor_t acceptor(ios, ep); boost::asio::ip::tcp::iostream tcp_stream; acceptor.accept(*tcp_stream.rdbuf()); tcp_stream << "hello\r\n" << endl; ... } return 0;View Code
8、UDP通信
UDP是無連接的,所以通信前不需要建立連接,使用send_to()和receive_from()來收發數據。
UDP服務端示例:
#include <iostream>
using namespace std;
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::udp::socket socket_t;
typedef boost::asio::ip::udp::endpoint endpoint_t;
typedef boost::asio::ip::udp udp_t;
typedef boost::system::error_code error_code_t;
typedef boost::asio::ip::address address_t;
int main()
{
cout << "udp server start." << endl;
io_service_t ios;
socket_t sock(ios, endpoint_t(udp_t::v4(), 6699));
for (;;)
{
char buf[2] = {0};
endpoint_t ep;
error_code_t ec;
sock.receive_from(boost::asio::buffer(buf), ep, 0, ec);
if (ec && ec != boost::asio::error::message_size)
{
throw boost::system::system_error(ec);
}
cout << "recv from " << ep.address() << ": " << buf << endl;
}
return 0;
}
View Code
UDP客戶端示例:
#include <iostream>
using namespace std;
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\lexical_cast.hpp"
#include "boost\bind.hpp"
typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::udp::socket socket_t;
typedef boost::asio::ip::udp::endpoint endpoint_t;
typedef boost::asio::ip::udp udp_t;
typedef boost::system::error_code error_code_t;
typedef boost::asio::ip::address address_t;
int main()
{
cout << "udp client start." << endl;
io_service_t ios;
socket_t sock(ios);
sock.open(udp_t::v4());
char buf[1];
buf[0] = ‘a‘;
endpoint_t send_ep(address_t::from_string("127.0.0.1"), 6699);
sock.send_to(boost::asio::buffer(buf), send_ep);
sock.close();
getchar();
return 0;
}
View Code
9、串口通信
aiso也支持串口通信,通過使用serial_port類。
boost--asio