0%

Introduction to Boost Asio

雖然有用過libuv,不過Boost asio可能會成為下一代C++的標準配備,還是先熟悉一下。
Boost除了Network之外,還有其他很多功能,不過此次就先以Network作介紹。
它同時支援Sync / Async兩種方式,以下是個範例。

Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <boost/asio.hpp>

using namespace boost::asio;
using namespace boost::system;
using namespace std;
int main(int argc, char* argv[])
{
io_service iosev;
ip::tcp::acceptor acceptor(iosev,
ip::tcp::endpoint(ip::tcp::v4(), 10000));
char buf[100];
for(;;)
{
ip::tcp::socket socket(iosev);
acceptor.accept(socket);
cout << socket.remote_endpoint().address() << endl;
error_code ec;
size_t len = socket.read_some(buffer(buf, 100), ec);
if (ec)
{
cout << system_error(ec).what() << endl;
break;
}
if (memcmp(buf, "Hello server", len) == 0) {
socket.write_some(buffer("Hello client"), ec);
if (ec) {
cout << system_error(ec).what() << endl;
break;
}
}
}
return 0;
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <boost/asio.hpp>
using namespace boost::asio;
using namespace boost::system;
using namespace std;
int main(int argc, char* argv[])
{
io_service iosev;
ip::tcp::socket socket(iosev);
ip::tcp::endpoint ep(ip::address_v4::from_string("127.0.0.1"), 10000);
error_code ec;
size_t len;
socket.connect(ep, ec);
if (ec) {
cout << system_error(ec).what() << endl;
return -1;
}
char buf[100];
len = socket.write_some(buffer("Hello server"), ec);
if (ec) {
cout << system_error(ec).what() << endl;
return -1;
}
len = socket.read_some(buffer(buf, 100), ec);
if (ec)
{
cout << system_error(ec).what() << endl;
return -1;
}
cout << buf << endl;
return 0;
}

看起來跟一般的Sample相差無幾,不過優點就是幫忙斃掉了其他不必要的麻煩。例如跨平台的問題,Header dependency之類令人懊惱的小事。不用煩惱為什麼Winsock2.h要放在Windows.h前面這種踳問題了

Async IO Operation

試著把Client用Async IO的方式重寫了一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
include <iostream>
#include <boost/asio.hpp>
using namespace boost::asio;
using namespace boost::system;
using namespace std;
io_service iosev;
ip::tcp::socket socket_(iosev);
static const size_t bufLen = 100;
char buf[bufLen];
void DoClose()
{
socket_.close();
}
void OnReceive(const error_code& ec, size_t bytes_transferred)
{
if (ec) {
cout << system_error(ec).what() << endl;
DoClose();
} else {
cout << buf << endl;
}
}
void OnSend(const error_code& ec, size_t bytes_transferred)
{
if (ec) {
cout << system_error(ec).what() << endl;
DoClose();
} else {
socket_.async_read_some(buffer(buf, bufLen), OnReceive);
}
}
void OnConnect(const boost::system::error_code& ec)
{
if (ec) {
cout << system_error(ec).what() << endl;
} else {
socket_.async_send(buffer("Hello server"), OnSend);
}
}
int main(int argc, char* argv[])
{
ip::tcp::endpoint ep(ip::address_v4::from_string("127.0.0.1"), 10000);
socket_.async_connect(ep, OnConnect);
iosev.run();
return 0;
}

因為這段時間都跟libuv打交道,所以隊這樣的程式碼沒有什麼問題。效率不論,跟libuv的主要的差別在於兩點。

  • libuv下read_start之後就會一直餵資料給你,直到你手動下read_stop。而asio如果要得到下一次資料的話,需要手動指定下一次讀取的動作。
  • libuv read callback function被呼叫的時候,必須手動將資料拷貝到你想儲存的地方,而 asio會主動存到你想要的地方。

其實就是API設計理念不同,沒有好壞之分。只是使用食要小心注意。