boost.asio系列——socket程式設計
asio的主要用途還是用於socket程式設計,本文就以一個tcp的daytimer服務為例簡單的演示一下如何實現同步和非同步的tcp socket程式設計。
客戶端
客戶端的程式碼如下:
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
tcp::endpoint end_point(boost::asio::ip::address::from_string("127.0.0.1"),
3200);
socket.connect(end_point);
for (;;)
{
boost::array<char, 128> buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf),
error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout.write(buf.data(), len);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
主要流程如下:
- 通過tcp::socket類定義一個tcp client物件socket
- 通過connect函式連線伺服器,開啟socket連線。
- 通過read_some函式來讀資料
另外,還可以通過write_some來寫資料,通過close來關閉socket連線(這裡是通過釋放socket物件隱式釋放連線)。
伺服器
伺服器程式碼如下:
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using namespace boost;
using boost::asio::ip::tcp;
int main()
{
try
{
asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(),
3200));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
time_t now = time(0);
std::string message = ctime(&now);
system::error_code ignored_error;
socket.write_some(asio::buffer(message), ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
主要流程如下:
- 通過tcp::acceptor類建立一個tcp server物件,並繫結埠(也可以不在構造器中自動繫結,而通過bind函式手動繫結)
- 通過accept函式獲取遠端連線
- 通過遠端連線的write_some函式將資料發往客戶端
非同步伺服器
前面的伺服器是同步版本,在大併發的場景下一般需要用到非同步socket。伺服器的非同步版本如下:
#include <ctime>
#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using namespace std;
void process_client(shared_ptr<tcp::socket> client)
{
time_t now = time(0);
shared_ptr<string>
message(new string(ctime(&now)));
auto callback = [=](const boost::system::error_code& err ,size_t size)
{
if ((int)size == message->length())
cout << "write completed" << endl;
};
client->async_send(boost::asio::buffer(*message),
callback);
}
typedef function<void (const boost::system::error_code&)> accept_callback;
void start_accept(tcp::acceptor& server)
{
shared_ptr<tcp::socket>
client(new tcp::socket(server.get_io_service()));
accept_callback callback = [&server, client](const boost::system::error_code& error)
{
if (!error)
process_client(client);
start_accept(server);
};
server.async_accept(*client,
callback);
}
int main()
{
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(),
3200));
start_accept(acceptor);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
這個非同步版本的邏輯倒不是很複雜,基本上和.net中傳統的非同步socket相似,不過需要注意的是,由於c++中記憶體需要自己管理,而asio框架也沒有提供任何管理機制,因此需要注意async_accept、async_send等函式的引數生命週期,切記不能在裡面傳入棧變數的引用。如果是堆變數,需要確保釋放,本例中我是通過share_ptr來實現的自動釋放。
更多的示例請參看asio官方文件。