前言
從大二開始就對服務器開發,感興趣,從Qt的QTcpServer和QTcpSocket到使用linux的epoll。後來有因爲工作搞了一個nginx和php做app後臺,後來想找個遊戲服務器方面的工作,可是人家嫌你沒開發經驗,當時我畢業還沒一年,能有多少錘子經驗。問我熟悉boost.asio庫嗎,我說知道,沒用過,然後就沒有然後啦。老老實實做嵌入式開發。
echo服務器基本是學習網絡編程裏的”Hello world”。所以今天就拿它開刀。
事件驅動
服務器是一個事件驅動的運行形式,那麼整個的事件調度就必須有東西來管理,什麼時候該可以讀,什麼時候是能寫的,那麼就是boost.asio中的io_service類,其是線程安全的,就是多個線程可以共同操作它。
網絡編程離不開socket,boost.asio中的ip::tcp::socket是對其的封裝,想將其加到事件循環中,就是綁定到一個io_service上,在初始化的時候當成一個參數傳遞給它。
異步操作
nginx如此高性能得益於其異步操作,其是Reactor模式,數據的讀寫不是異步的,boost.asio是Proactor模式,數據的讀寫是異步的,但是看文檔你會發現,在linux平臺其實現是Reactor,在window上得益於iocp,實現了讀寫異步。不使用協程的前提下,使用回調來實現,異步操作通知。
代碼如下:
TestEchoConnection封裝了一個客戶連接,TestEchoServer是echo服務器。
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/bind/placeholders.hpp>
#include <boost/system/system_error.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <list>
using namespace boost::asio;
typedef boost::shared_ptr<ip::tcp::socket> socket_ptr;
class TestEchoConnection;
typedef boost::shared_ptr<TestEchoConnection> TestEchoConnection_ptr;
typedef std::list<TestEchoConnection_ptr> ConnectionManager;
class TestEchoConnection : public boost::enable_shared_from_this<TestEchoConnection>
, private boost::noncopyable
{
public:
TestEchoConnection(io_service &server, ConnectionManager &connectManager)
: m_server(server)
, m_connectManager(connectManager)
, m_socket(new ip::tcp::socket(m_server))
{
}
~TestEchoConnection()
{
std::cout << "~TestEchoConnection" << std::endl;
}
ip::tcp::socket& socket()
{
return *m_socket;
}
void start()
{
m_socket->async_read_some(buffer(m_buffer), boost::bind(&TestEchoConnection::handle_read, shared_from_this(),
_1, _2));
}
void stop()
{
m_socket->close();
m_connectManager.remove(TestEchoConnection_ptr(shared_from_this()));
}
private:
void handle_read(const boost::system::error_code &ec, size_t data_size)
{
if (!ec) {
std::cout << "handle_read->data size:" << data_size << std::endl;
m_socket->async_write_some(buffer(m_buffer), boost::bind(&TestEchoConnection::handler_write,
shared_from_this(), _1));
// m_socket->async_read_some(buffer(m_buffer), boost::bind(&TestEchoConnection::handle_read, shared_from_this(),
// _1, _2));
}
else if (ec == error::operation_aborted || ec == error::eof) {
std::cout << "handle_read" << "remove" << std::endl;
stop();
}
}
void handler_write(const boost::system::error_code &ec)
{
if (!ec) {
memset(&m_buffer, 0, sizeof(m_buffer));
// m_socket->async_read_some(buffer(m_buffer), boost::bind(&TestEchoConnection::handle_read, shared_from_this(),
// _1, _2));
if (ec == error::operation_aborted) {
std::cout << "handler_write" << "remove" << std::endl;
stop();
}
}
stop();
}
private:
io_service& m_server;
ConnectionManager& m_connectManager;
socket_ptr m_socket;
boost::array<char, 1029> m_buffer;
};
class TestEchoServer : public boost::enable_shared_from_this<TestEchoServer>
, private boost::noncopyable
{
public:
TestEchoServer(io_service &service, int port)
: m_io_service(service)
, m_endpoint(ip::tcp::v4(), port)
, m_acceptor(m_io_service, m_endpoint)
{
}
~TestEchoServer()
{
}
void start_accept()
{
m_connect.reset(new TestEchoConnection(m_io_service, m_connectionManager));
m_acceptor.async_accept(m_connect->socket(), boost::bind(&TestEchoServer::handle_accept, shared_from_this(), _1));
}
private:
void handle_accept(const boost::system::error_code &err)
{
if (!err) {
m_connectionManager.push_back(m_connect);
m_connect->start();
}
start_accept();
}
private:
io_service& m_io_service;
ip::tcp::endpoint m_endpoint;
ip::tcp::acceptor m_acceptor;
ConnectionManager m_connectionManager;
TestEchoConnection_ptr m_connect;
};
// echo server
int main()
{
io_service service;
boost::shared_ptr<TestEchoServer> testserver(new TestEchoServer(service, 8090));
// TestEchoServer tesetserver(service, 8090);
// tesetserver.start_accept();
testserver->start_accept();
service.run();
return 0;
}
io_service::run()方法在有異步任務的時候是不會退出的,爲了我們的服務器能夠一直運行,所有必須在異步任務的回調中在添加一個異步任務,爲了我可以使用web壓力工具測試它,我在echo輸出之後就關閉了這個連接,這樣我的siege才能工作。
siege 127.0.0.1:8090 -t20s -c100
結果:
Lifting the server siege... done.
Transactions: 3834 hits
Availability: 100.00 %
Elapsed time: 19.38 secs
Data transferred: 3.23 MB
Response time: 0.00 secs
Transaction rate: 197.83 trans/sec
Throughput: 0.17 MB/sec
Concurrency: 0.21
Successful transactions: 3834
Failed transactions: 0
Longest transaction: 0.02
Shortest transaction: 0.00