套接字 Sockets
Boost.Asio有三種類型的Socket類:ip::tcp,ip::udp和ip::icmp,三者都是可擴展的。你可以創建自己的Socket類,雖然做起來稍微複雜了點。假如你真的要這麼做,可以參考boost/asio/ip/tcp.hpp,boost/asio/ip/udp.hpp和boost/asio/ip/icmp.hpp。它們都是很小的類,在內部使用typedef關鍵字。
你可以把ip::tcp,ip::udp和ip::icmp類,作爲一個佔位符;可以用下面的形式來訪問類的和類的方法:
- ip::tcp::socket, ip::tcp::acceptor, ip::tcp::endpoint, ip::tcp::resolver, ip::tcp::iostream
- ip::udp::socket, ip::udp::endpoint, ip::udp::resolver
- ip::icmp::socket, ip::icmp::endpoint, ip::icmp::resolver
相應的socket類創建一個對應的套接字。在io_service實例構造的時候,你需要傳入這個socket:
io_service service;
ip::udp::socket sock(service);
sock.set_option(ip::udp::socket::reuse_address(true));
每一個socket的名字都由typedef而來:
- ip::tcp::socket = basic_stream_socket
- ip::udp::socket = basic_stream_socket
- ip::icmp::socket = basic_stream_socket
同步錯誤代碼(Synchronous error codes)
所有的同步函數都有例外情況,它們會拋出異常或者返回一個錯誤代碼,例如下列代碼:
sync_func(arg1, arg2 ... argN); //拋出異常
boost::system::error_code ec;
sync_func(arg1 arg2, ... , argN, ec); //返回錯誤代碼
在後面,會遇到很多同步函數。爲了保持簡潔,後面會忽略返回錯誤代碼的例外情況。
Socket成員函數
這些函數被分爲幾個組。並不是每個種類的socket都能訪問所有這些成員函數。本節的最後,會有一張表列出哪些成員是隸屬於何種socket類。
需要注意的是,所有的異步函數都是立即返回的,而同步版本的函數,只在操作完成之後才返回。
連接相關的類
有些函數用於連接或者綁定socket,斷開連接,查詢連接是否有效:
- assign(protocol, socket):它給socket實例賦予原始套接字(原生)。使用它來處理一些遺留系統(這些系統中套接字通常是已經創建過了的)。
- open(protocol):用給定的IP協議(v4或者v6)打開一個socket。主要用於UDP/ICMP socket,或者是用於服務器端socket。
- bind(endpoint):綁定到指定的地址。
- connect(endpoint):同步連接到指定的地址。
- async_connect(endpoint):異步地連接到指定的地址。
- is_open():socket是否是打開的。
- close():關閉套接字。任何在此socket的異步操作都會被取消掉,並以error::operation_aborted錯誤代碼完成。
- shutdown(type_of_shutdown):禁用send,receive操作。
- cancel():取消socket上的所有異步操作。所有在此socket上的異步操作都會被立即完成,並以error::operation_aborted錯誤代碼返回。
下面是例子:
ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 80);
ip::tcp::socket sock(service);
sock.open(ip::tcp::v4());
sock.connect(ep);
sock.write_some(buffer("GET /index.html\r\n"));
char buff[1204];
sock.read_some(buffer(buff, 1024));
sock.shutdown(ip::tcp::socket::shutdown_receive);
sock.close();
讀/寫函數
讀寫函數在socket上進行I/O操作。
對於異步函數,handler是一個回調函數,形如void handler(const boost::system::error_code& e, size_t bytes)。
- async_receive(buffer, [flags], handler):在socket上開始異步的recevie操作。
- async_read_some(buffer, handler):和async_receive作用相同。
- async_receive_from(buffer, endpoint [,flags], handler:在指定的endpoint上開始異步的receive操作。
- async_send(buffer [, flags], handler):將緩衝區中的數據異步的發送出去。
- async_write_some(buffer, handler):和async_send相同。
- async_send_to(buffer, endpoint, handler):在指定的endpoint上開始異步的發送操作。
- receive(buffer [, flags]):同步接受數據到buffer,將阻塞,知道數據接收完成,或者出錯。
- read_some(buffer):和receive相同。
- receive_from(buffer, endpoint [, flags]):從給定的endpoint接收數據,將阻塞,知道數據接收完成,或者出錯。
- send(buffer [, flags]):同步的發送緩衝區中的數據,將阻塞,知道數據發送完成,或者出錯。
- write_some(buffer):和send相同。
- send_to(buffer, endpoint [, flags]):同步地將數據發送給指定endpoint。將阻塞,知道數據發送完成,或者出錯。
- available():返回在不阻塞的情況下,可以從socket中讀取出多少字節。
這裏簡單討論下緩衝區。默認的flags參數是0,也可以混合下面的值:
- ip::socket_type::socket::message_peek:這個flag表示,只在緩衝區中檢索數據。它可以返回消息,但是下一次讀時,會重新讀取這條數據。
- ip::socket_type::socket::message_out_of_band:這個標誌表示處理帶外數據(OBB)。OBB數據比普通的數據更重要。對OBB數據的討論超出了本書的範圍。
- ip::socket_type::socket::message_end_of_record:Windows下不支持。
大多數時候我們都使用message_peek,例子如下:
char buff[1024];
sock.receive(buffer(buff), ip::tcp::socket::message_peek);
memset(buff, 1024, 0);
//重新讀取上次讀取過的數據
sock.receive(buffer(buff));
下面的例子演示了同步和異步的區別:
- 例子1,在tcp socket上同步的寫和讀:
ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1", 80);
ip::tcp::socke sock(service);
sock.connect(ep);
sock.write_some(buffer("GET /index.html\r\n");
std::cout<< "有效字節數: "<< sock.available() <<std::endl;
char buff[512];
size_t read = sock.read_some(buffer(buff));
- 例子2,在UDP socket上異步讀和寫:
ip::udp::socket sock(service);
sock.open(ip::udp::v4());
ip::udp::endpoint receiver_ep("xxx.xxx.xxx.xxx", 80);
sock.send_to(buffer("testing\n"), receiver_ep);
char buff[512];
ip::udp::endpoint sender_ep;
sock.receive_from(buffer(buff), sender_ep);
- 例子3,從UDP服務器socket中異步讀取:
using namespace boost::asio;
io_service service;
ip::udp::socket sock(service);
boost::asio::ip::udp::endpoint sender_ep;
char buff[512];
void on_read(const boost::system::error_code& err, std::size_t read_bytes)
{
std::cout<<"read "<< read_bytes << std::endl;
sock.async_receive_from(buffer(buff), sender_ep, on_read);
}
int main(int argc, char** argv)
{
ip::udp::endpoint ep(ip::address::from_string("127.0.0.1"), 8001);
sock.open(ep.protocol());
sock.set_option(boost::asio::ip::udp::socket::reuse_address(true));
sock.bind(ep);
sock.async_receive_from(buffer(buff, 512), sender_ep, on_read);
service.run();
return 0;
}