1.2.6 流,短讀和短寫
Asio中的許多I / O對象都是面向流的。 這意味著:
- 沒有消息邊界。 正在傳輸的數據是連續的字節序列。
- 讀或寫操作可能會傳輸比請求更少的字節。 這被稱為短讀或短寫。
提供面向流的I / O模型的對象有一個或多個以下類型的需求:
- SyncReadStream,其中使用名為read_some()的成員函數執行同步讀取操作。
- AsyncReadStream,使用名為async_read_some()的成員函數執行異步讀取操作
- SyncWriteStream,其中使用名為write_some()的成員函數執行同步寫入操作
- AsyncWriteStream,其中使用稱為async_write_some()的成員函數執行異步寫入操作。
面向流的I / O對象的例子包括ip :: tcp :: socket,ssl :: stream <>,
posix :: stream_descriptor,windows :: stream_handle等
程序通常希望傳輸確切的字節數。 當發生短讀或短寫時,程序必須重新開始操作,并繼續這樣做直到傳送所需的字節數。 Asio提供了自動執行此操作的泛型函數:read(),async_read(),write()和async_write()。
為什么EOF是一個錯誤
- 流的結束可能導致讀取,async_read,read_until或async_read_until函數違反其合約。 例如。 由于EOF,N字節的讀取可能會提前結束
- EOF錯誤可以用來區分一個流的結束或者是成功讀取大小為0的流
1.2.7反應堆式作業
有時一個程序想要自己執行I / O操作必須與第三方庫集成。 為了實現這一點,Asio包含一個null_buffers類型,可用于讀取和寫入操作。 null_buffers操作直到I / O對象“準備就緒”才能執行操作。
作為一個例子,可以使用如下所示的非阻塞式讀取:
ip::tcp::socket socket(my_io_service);
...
socket.non_blocking(true);
...
socket.async_read_some(null_buffers(), read_handler);
...
void read_handler(asio::error_code ec)
{
if (!ec)
{
std::vector<char> buf(socket.available());
socket.read_some(buffer(buf));
}
}
所有平臺上的套接字以及POSIX面向流的描述符類都支持這些操作。
1.2.8 基于行的操作
許多常用的互聯網協議是基于行的,這意味著它們具有由字符序列“\ r \ n”分隔的協議元素。 例子包括HTTP,SMTP和FTP。 為了更容易地實現基于行的協議以及使用分隔符的其他協議,Asio包含函數read_until()和async_read_until()。
以下示例說明如何在HTTP服務器中使用async_read_until()來接收來自客戶端的HTTP請求的第一行:
class http_connection
{
...
void start()
{
asio::async_read_until(socket_, data_, "\r\n",
boost::bind(&http_connection::handle_request_line, this, _1));
}
void handle_request_line(asio::error_code ec)
{
if (!ec)
{
std::string method, uri, version;
char sp1, sp2, cr, lf;
std::istream is(&data_);
is.unsetf(std::ios_base::skipws);
is >> method >> sp1 >> uri >> sp2 >> version >> cr >> lf;
...
}
}
...
asio::ip::tcp::socket socket_;
asio::streambuf data_;
};
streambuf數據成員用作存儲從套接字讀取的數據的位置,然后再搜索分隔符。 請務必記住,分隔符后面可能還有其他數據。 這個剩余數據應該留在
streambuf,以便可以通過對read_until()或async_read_until()的后續調用進行檢查。
分隔符可以被指定為單個字符,std :: string或boost :: regex。 read_until()和async_read_until()函數還包含接受用戶定義的函數對象(稱為匹配條件)的重載。 例如,要將數據讀取到streambuf中,直到遇到空白為止:
typedef asio::buffers_iterator<asio::streambuf::const_buffers_type> iterator;
std::pair<iterator, bool>match_whitespace(iterator begin, iterator end)
{
iterator i = begin;
while (i != end)
if (std::isspace(*i++))
return std::make_pair(i, true);
return std::make_pair(i, false);
}
...
asio::streambuf b;
asio::read_until(s, b, match_whitespace);
要將數據讀入streambuf直到找到匹配的字符:
class match_char
{
public:
explicit match_char(char c) : c_(c) {}
template <typename Iterator>
std::pair<Iterator, bool> operator()(
Iterator begin, Iterator end) const
{
Iterator i = begin;
while (i != end)
if (c_ == *i++)
return std::make_pair(i, true);
return std::make_pair(i, false);
}
private:
char c_;
};
namespace asio {
template <> struct is_match_condition<match_char>
: public boost::true_type {};
} // namespace asio
...
asio::streambuf b;
asio::read_until(s, b, match_char(’a’));
is_match_condition <>類型trait會自動為函數計算true,并為具有嵌套result_type typedef的函數對象自動計算該值。 對于其他類型,特質必須明確專用,如上所示。
1.2.9 自定義內存分配
許多異步操作需要分配一個對象來存儲與操作相關的狀態。 例如,Win32實現需要OVERLAPPED派生的對象傳遞給Win32 API函數。
此外,程序通常包含易于識別的異步操作鏈。 半雙工協議實現(例如,HTTP服務器)將為每個客戶端具有單個操作鏈(接收后接發送)。 全雙工協議實現將有兩個鏈并行執行。 程序應該能夠利用這些知識為鏈中的所有異步操作重用內存。
給定一個用戶定義的Handler對象h的副本,如果實現需要分配與該處理程序關聯的內存,它將執行代碼:
void* pointer = asio_handler_allocate(size, &h);
同樣,為了釋放它將執行的內存:
asio_handler_deallocate(pointer, size, &h);
它們分別用:: operator new()和:: operator delete()來實現。
該實現保證解除分配將在調用相關處理程序之前發生,這意味著內存已準備好重新用于處理程序啟動的任何新的異步操作。
自定義內存分配函數可以從調用庫函數的任何用戶創建的線程中調用。 該實現保證,對于包含該庫的異步操作,該實現不會并發調用該處理程序的內存分配函數。 如果需要從不同的線程調用分配函數,該實現將插入適當的內存屏障以確保正確的內存可見性。