引言
寸光陰,當下我們或許更需要利用現有的知識,應用現有的技術。網絡是當前互聯網的根本,瞭解網絡便開始顯得極其重要。今天我們利用Boost庫中Asio部分,淺嘗網絡服務器。此處不做過於深入的開展,爲達成學習目的,只做簡單的異步併發服務器。
注意:本篇代碼沒有直接引用boost等命名空間,爲的是新入門Boost的同學能夠更好的瞭解每個參數在boost的具體命名空間位置,有助於更好的理解boost的佈局。
版權所有:_OE_,轉載請註明出處:http://blog.csdn.net/csnd_ayo
碼雲源代碼下載:https://git.oschina.net/Mr_ChenLuYong
CSDN代碼下載:http://download.csdn.net/detail/csnd_ayo/9787966
服務器用例
我們在做服務器之前,首先細想一下,服務器應具備哪些基本特質。
1、構建:一個服務器應該具備被連接的IP地址(網絡地址)、可以被訪問的Port(端口號)
2、聆聽:服務器應該能夠實時處理基本的連接請求
3、處理:交互纔是目的,可以與客戶端實現基本的交互
4、異步:處理客戶端的請求時,不會因爲客戶端的延遲響應而導致程序假死
建造(Build)
電腦裏有非常多的端口,而客戶端只會把消息傳到約定的地址與端口,只有在正確的端口等待,才能接到自己預期的客戶。
就好像樓房裏有非常多層樓一樣,而快遞員只會把物品送到約定的樓層,只有在正確的樓層等待,才能達成預期的結果。
#include <iostream> #include <boost/asio.hpp> int main(void) { try { std::cout << "server start." << std::endl; // asio程序必須的io_service對象 boost::asio::io_service ios; // 具體的服務器地址與端口 boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695); // 創建acceptor對象,當前的IPV4作爲服務器地址(127.0.0.1 || 0.0.0.0),接受端口13695的消息. boost::asio::ip::tcp::acceptor acceptor(ios, endpotion); // 打印當前服務器地址 std::cout << "addr: " << acceptor.local_endpoint().address() << std::endl; // 打印當前服務器端口 std::cout << "port: " << acceptor.local_endpoint().port() << std::endl; } catch (...) { std::cout << "server exceptional." << std::endl; } std::cout << "server end." << std::endl; getchar(); return 0; }
聆聽(Listen)
一個基本的連接,在正常的情況下,應該由客戶端發起,服務器應該處於實時監聽的狀態,因爲能接到客戶端發起的連接請求,這纔是網絡操作的根本。
#include <iostream> #include <boost/asio.hpp> int main(void) { try { std::cout << "server start." << std::endl; // asio程序必須的io_service對象 boost::asio::io_service ios; // 具體的服務器地址與端口 boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695); // 創建acceptor對象,當前的IPV4作爲服務器地址(127.0.0.1 || 0.0.0.0),接受端口13695的消息. boost::asio::ip::tcp::acceptor acceptor(ios, endpotion); // 打印當前服務器地址 std::cout << "addr: " << acceptor.local_endpoint().address() << std::endl; // 打印當前服務器端口 std::cout << "port: " << acceptor.local_endpoint().port() << std::endl; // 循環執行服務 while (true) { // 一個臨時的socket對象 boost::asio::ip::tcp::socket socket(ios); // 阻塞等待客戶端連接,連接成功後返回socket, accept這個函數使用引用來調取socket. acceptor.accept(socket); // 打印與本機服務器取得連接的客戶端IP地址 std::cout << "client: " << socket.remote_endpoint().address() << std::endl; } } catch (std::exception& _e) { std::cout << "server exceptional." << std::endl; std::cout << _e.what() << std::endl; } std::cout << "server end." << std::endl; getchar(); return 0; }
處理(Operation)
一旦服務器收到客戶端發起的連接請求,便爲客戶端建立服務。與客戶端建立連接的目的,始終是爲了交互,我們不能本末倒置。
我們嘗試一下,第一次交互的滋味。
#include <iostream> #include <boost/asio.hpp> int main(void) { try { std::cout << "server start." << std::endl; // asio程序必須的io_service對象 boost::asio::io_service ios; // 具體的服務器地址與端口 boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695); // 創建acceptor對象,當前的IPV4作爲服務器地址(127.0.0.1 || 0.0.0.0),接受端口13695的消息. boost::asio::ip::tcp::acceptor acceptor(ios, endpotion); // 打印當前服務器地址 std::cout << "addr: " << acceptor.local_endpoint().address() << std::endl; // 打印當前服務器端口 std::cout << "port: " << acceptor.local_endpoint().port() << std::endl; // 循環執行服務 while (true) { // 一個臨時的socket對象 boost::asio::ip::tcp::socket socket(ios); // 阻塞等待客戶端連接,連接成功後返回socket, accept這個函數使用引用來調取socket. acceptor.accept(socket); // 打印與本機服務器取得連接的客戶端IP地址 std::cout << "client: " << socket.remote_endpoint().address() << std::endl; //////////////////////////////處理///////////////////////////////// std::string msg; // 阻塞發送作者名稱到客戶端 socket.write_some(boost::asio::buffer("hello CSND_Ayo")); // 阻塞接收客戶端發來的數據 socket.read_some(boost::asio::buffer(msg)); // 打印客戶端發來的數據 std::cout << "client reply: " << msg.c_str() << std::endl; } } catch (std::exception& _e) { std::cout << "server exceptional." << std::endl; std::cout << _e.what() << std::endl; } std::cout << "server end." << std::endl; getchar(); return 0; }
異步(Async)
處理客戶端的請求時,不會因爲客戶端的延遲響應而導致程序假死
#include <iostream> #include <boost/asio.hpp> #include <boost/bind.hpp> // 異步服務器類 class Server { private: // 服務實例 boost::asio::io_service& ios_; // 接收器實例 boost::asio::ip::tcp::acceptor acceptor_; // socket智能指針 typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr; public: Server(boost::asio::io_service& _ios) : ios_(_ios), acceptor_(_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 13695)) { // 默認執行 start(); } // 啓動網絡偵聽的操作入口 void start(void) { // 自定義的智能指針 socket_ptr socket(new boost::asio::ip::tcp::socket(ios_)); // 異步偵聽,若有服務連接,則自動調用Server::handler_accept函數,並將error, socket傳入作爲參數 acceptor_.async_accept(*socket, boost::bind(&Server::accept_handler, this, boost::asio::placeholders::error/* 此處作爲佔位符 */, socket)); } // 請求者響應後觸發的處理器 void accept_handler(const boost::system::error_code& _ec, socket_ptr _socket) { // 錯誤碼檢測 if (_ec) { return; } // 打印當前連接進來的客戶端 std::cout << "client: " << _socket->remote_endpoint().address() << std::endl; // 異步發送 "hello CSND_Ayo" 消息到客戶端,發送成功後,自動調用Server::write_handler函數 _socket->async_write_some(boost::asio::buffer("hello CSND_Ayo"), boost::bind(&Server::write_handler, this, boost::asio::placeholders::error/* 此處作爲佔位符 */)); // 啓動新的異步監聽 start(); } // 完成異步寫操作後的處理器 void write_handler(const boost::system::error_code& _ec) { std::cout << "server: send message complete." << std::endl; } }; int main(void) { try { std::cout << "server start." << std::endl; // 建造服務對象 boost::asio::io_service ios; // 構建Server實例 Server server(ios); // 啓動異步調用事件處理循環 ios.run(); } catch (std::exception& _e) { std::cout << _e.what() << std::endl; } std::cout << "server end." << std::endl; return 0; }
作者的簡易併發服務器類
使用兩個類來撰寫了一個併發的服務器類
Server(服務器監聽類)、Session(會話類)
具備功能:
1、異步監聽客戶端連接
2、客戶連接時,首包要求具有特定格式(協議包)
3、併發處理客戶端交互
當前類的網絡交互協議拓撲圖
Server.h
#ifndef __CLY_SERVER_H__ #define __CLY_SERVER_H__ #include <string.h> #include <boost/asio.hpp> #include <boost/shared_ptr.hpp> class Session; class Server { private: // 會話 - 智能指針 typedef boost::shared_ptr<Session> session_ptr; public: Server(boost::asio::io_service &_ioService, boost::asio::ip::tcp::endpoint &_endpoint); virtual ~Server(void); // 監聽 void start(void); // 異步 void run(void); private: // 數據導出接口 void callback_session(std::string _fromIp, std::string _info); // 會話啓動 void accept_handler(session_ptr _chatSession, const boost::system::error_code& _error); private: boost::asio::io_service &ioService_; boost::asio::ip::tcp::acceptor acceptor_; }; #endif // __CLY_SERVER_H__
Server.cpp
#include <boost/bind.hpp> #include "Server.h" #include "Session.h" Server::Server(boost::asio::io_service &_ioService, boost::asio::ip::tcp::endpoint &_endpoint) : ioService_(_ioService), acceptor_(_ioService, _endpoint) { start(); } Server::~Server(void) { } void Server::start(void) { session_ptr new_chat_session(new Session(ioService_)); acceptor_.async_accept(new_chat_session->socket(), boost::bind(&Server::accept_handler, this, new_chat_session, boost::asio::placeholders::error)); } void Server::run(void) { ioService_.run(); } void Server::callback_session(std::string /*_fromIp*/, std::string /*_info*/) { return; } void Server::accept_handler(session_ptr _chatSession, const boost::system::error_code& _error) { if (!_error && _chatSession) { try { _chatSession->start(); start(); } catch (...) { return; } } }
Session.h
#ifndef __CLY_SESSION_H__ #define __CLY_SESSION_H__ #include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/enable_shared_from_this.hpp> #define REPLY_SIZE (32) // 會話類 class Session : public boost::enable_shared_from_this<Session> { public: typedef void pSessionCallback(std::string, std::string); public: Session(boost::asio::io_service& _ioService); virtual ~Session(void); void start(void); void setCallback(pSessionCallback* _callback) { callback_ = _callback; } // socket 實例 boost::asio::ip::tcp::socket& socket(void); private: // 第一個協議包 void init_handler(const boost::system::error_code& _error); // 解析協議包 void analyse_handler(const boost::system::error_code& _error); // 完成數據傳輸後觸發的收尾工作 void done_handler(const boost::system::error_code& _error); // 讀取成功後觸發的函數 void read_handler(const boost::system::error_code& _error, size_t _readSize); // 寫入完成後觸發的函數 void write_handler(const boost::system::error_code& _error); private: // 臨時信息緩衝區 char msg_[1024]; std::string currentMsg_; // 數據總數量 int sumSize_; // 單個數據包大小 unsigned int maxSize_; // socket句柄 boost::asio::ip::tcp::socket socket_; // 回調 pSessionCallback* callback_; }; #endif // __CLY_SESSION_H__
Session.cpp
#include <boost/bind.hpp> #include "Session.h" Session::Session(boost::asio::io_service& _ioService) :socket_(_ioService) { memset(msg_, 0, sizeof(msg_)); } Session::~Session(void) { } void Session::start(void) { // 告訴鏈接成功的客戶端,你想要的信息。 char msg[256] = "001:Connect Succeed! Please tell me with 10 bytes, the total data and the size of each package, example:128 1024"; boost::asio::async_write(socket_, boost::asio::buffer(msg, strlen(msg)), boost::bind(&Session::init_handler, shared_from_this(), boost::asio::placeholders::error)); } boost::asio::ip::tcp::socket& Session::socket(void) { return socket_; } // 第一個協議包 void Session::init_handler(const boost::system::error_code& _error) { if (_error) { return; } // 讀取客戶端發來的 10 bytes,確定單個包的大小以及數據總大小 boost::asio::async_read(socket_, boost::asio::buffer(msg_, 10), boost::bind(&Session::analyse_handler, shared_from_this(), boost::asio::placeholders::error)); } void Session::analyse_handler(const boost::system::error_code& _error) { if (_error) { return; } // 分析協議包格式 bool bflag = true; // 正則分析格式 // do something. if (!bflag) { start(); return; } // 格式化保存協議包數據 std::stringstream io(msg_); io >> maxSize_; io >> sumSize_; // 發送接收請求信息 char msg[REPLY_SIZE]; sprintf_s(msg, "001:is ok, data remaining %d.", sumSize_); boost::asio::async_write(socket_, boost::asio::buffer(msg, REPLY_SIZE), boost::bind(&Session::write_handler, shared_from_this(), boost::asio::placeholders::error)); } // 完成數據傳輸 void Session::done_handler(const boost::system::error_code& _error) { if (_error) { return; } currentMsg_ += msg_; // 發送信息到回調 if (!currentMsg_.empty() && callback_ != nullptr) { callback_(socket_.remote_endpoint().address().to_string(), currentMsg_); currentMsg_.clear(); } memset(msg_, 0, sizeof(msg_)); char msg[32] = "001:will done."; boost::asio::async_write(socket_, boost::asio::buffer(msg, REPLY_SIZE), boost::bind(&Session::init_handler, shared_from_this(), boost::asio::placeholders::error)); } void Session::read_handler(const boost::system::error_code& _error, size_t _readSize) { if (_error) { return; } // 數據處理 currentMsg_ += msg_; if (currentMsg_.size() > 1024 * 512) { // 發送信息到回調 if (callback_ != nullptr) { callback_(socket_.remote_endpoint().address().to_string(), currentMsg_); currentMsg_.clear(); } } memset(msg_, 0, sizeof(msg_)); // 計算當前剩餘數據數量 sumSize_ -= _readSize; // 接收完成 if (0 > sumSize_) { done_handler(_error); } // 繼續接收 else { char msg[REPLY_SIZE]; sprintf_s(msg, "001:%d.", sumSize_); boost::asio::async_write(socket_, boost::asio::buffer(msg, REPLY_SIZE), boost::bind(&Session::write_handler, shared_from_this(), boost::asio::placeholders::error)); std::cout << "send client recv succeed: " << msg << std::endl; } } void Session::write_handler(const boost::system::error_code& _error) { if (_error) { return; } boost::asio::async_read(socket_, boost::asio::buffer(msg_, maxSize_), boost::bind(&Session::read_handler, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); }
main.cpp
#include <iostream> #include <boost/asio.hpp> #include <boost/bind.hpp> #include "Server.h" int main(void) { try { std::cout << "server start." << std::endl; // 建造服務對象 boost::asio::io_service ios; // 具體的服務器地址與端口 boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695); // 構建Server實例 Server server(ios, endpotion); // 啓動異步調用事件處理循環 server.run(); } catch (std::exception& _e) { std::cout << _e.what() << std::endl; } std::cout << "server end." << std::endl; return 0; }