1. 程式人生 > >C++ 建立本地網路伺服器 (Boost.Asio庫)

C++ 建立本地網路伺服器 (Boost.Asio庫)

引言

寸光陰,當下我們或許更需要利用現有的知識,應用現有的技術。網路是當前網際網路的根本,瞭解網路便開始顯得極其重要。今天我們利用Boost庫中Asio部分,淺嘗網路伺服器。此處不做過於深入的開展,為達成學習目的,只做簡單的非同步併發伺服器。

注意:本篇程式碼沒有直接引用boost等名稱空間,為的是新入門Boost的同學能夠更好的瞭解每個引數在boost的具體名稱空間位置,有助於更好的理解boost的佈局。

伺服器用例

我們在做伺服器之前,首先細想一下,伺服器應具備哪些基本特質。

1、構建:一個伺服器應該具備被連線的IP地址(網路地址)、可以被訪問的Port(埠號)

2、聆聽:伺服器應該能夠實時處理基本的連線請求

3、處理:互動才是目的,可以與客戶端實現基本的互動

4、非同步:處理客戶端的請求時,不會因為客戶端的延遲響應而導致程式假死

建造(Build)

電腦裡有非常多的埠,而客戶端只會把訊息傳到約定的地址與埠,只有在正確的埠等待,才能接到自己預期的客戶。

就好像樓房裡有非常多層樓一樣,而快遞員只會把物品送到約定的樓層,只有在正確的樓層等待,才能達成預期的結果。

  1. #include <iostream>

  2. #include <boost/asio.hpp>

  3. int main(void) {

  4. try {

  5. std::cout << "server start." << std::endl;

  6. // asio程式必須的io_service物件

  7. boost::asio::io_service ios;

  8. // 具體的伺服器地址與埠

  9. boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695);

  10. // 建立acceptor物件,當前的IPV4作為伺服器地址(127.0.0.1 || 0.0.0.0),接受埠13695的訊息.

  11. boost::asio::ip::tcp::acceptor acceptor(ios, endpotion);

  12. // 列印當前伺服器地址

  13. std::cout << "addr: " << acceptor.local_endpoint().address() << std::endl;

  14. // 列印當前伺服器埠

  15. std::cout << "port: " << acceptor.local_endpoint().port() << std::endl;

  16. }

  17. catch (...) {

  18. std::cout << "server exceptional." << std::endl;

  19. }

  20. std::cout << "server end." << std::endl;

  21. getchar();

  22. return 0;

  23. }

聆聽(Listen)

一個基本的連線,在正常的情況下,應該由客戶端發起,伺服器應該處於實時監聽的狀態,因為能接到客戶端發起的連線請求,這才是網路操作的根本。

  1. #include <iostream>

  2. #include <boost/asio.hpp>

  3. int main(void) {

  4. try {

  5. std::cout << "server start." << std::endl;

  6. // asio程式必須的io_service物件

  7. boost::asio::io_service ios;

  8. // 具體的伺服器地址與埠

  9. boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695);

  10. // 建立acceptor物件,當前的IPV4作為伺服器地址(127.0.0.1 || 0.0.0.0),接受埠13695的訊息.

  11. boost::asio::ip::tcp::acceptor acceptor(ios, endpotion);

  12. // 列印當前伺服器地址

  13. std::cout << "addr: " << acceptor.local_endpoint().address() << std::endl;

  14. // 列印當前伺服器埠

  15. std::cout << "port: " << acceptor.local_endpoint().port() << std::endl;

  16. // 迴圈執行服務

  17. while (true) {

  18. // 一個臨時的socket物件

  19. boost::asio::ip::tcp::socket socket(ios);

  20. // 阻塞等待客戶端連線,連線成功後返回socket, accept這個函式使用引用來調取socket.

  21. acceptor.accept(socket);

  22. // 列印與本機伺服器取得連線的客戶端IP地址

  23. std::cout << "client: " << socket.remote_endpoint().address() << std::endl;

  24. }

  25. }

  26. catch (std::exception& _e) {

  27. std::cout << "server exceptional." << std::endl;

  28. std::cout << _e.what() << std::endl;

  29. }

  30. std::cout << "server end." << std::endl;

  31. getchar();

  32. return 0;

  33. }

處理(Operation)

一旦伺服器收到客戶端發起的連線請求,便為客戶端建立服務。與客戶端建立連線的目的,始終是為了互動,我們不能本末倒置。

我們嘗試一下,第一次互動的滋味。

  1. #include <iostream>

  2. #include <boost/asio.hpp>

  3. int main(void) {

  4. try {

  5. std::cout << "server start." << std::endl;

  6. // asio程式必須的io_service物件

  7. boost::asio::io_service ios;

  8. // 具體的伺服器地址與埠

  9. boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695);

  10. // 建立acceptor物件,當前的IPV4作為伺服器地址(127.0.0.1 || 0.0.0.0),接受埠13695的訊息.

  11. boost::asio::ip::tcp::acceptor acceptor(ios, endpotion);

  12. // 列印當前伺服器地址

  13. std::cout << "addr: " << acceptor.local_endpoint().address() << std::endl;

  14. // 列印當前伺服器埠

  15. std::cout << "port: " << acceptor.local_endpoint().port() << std::endl;

  16. // 迴圈執行服務

  17. while (true) {

  18. // 一個臨時的socket物件

  19. boost::asio::ip::tcp::socket socket(ios);

  20. // 阻塞等待客戶端連線,連線成功後返回socket, accept這個函式使用引用來調取socket.

  21. acceptor.accept(socket);

  22. // 列印與本機伺服器取得連線的客戶端IP地址

  23. std::cout << "client: " << socket.remote_endpoint().address() << std::endl;

  24. //////////////////////////////處理/////////////////////////////////

  25. std::string msg;

  26. // 阻塞傳送作者名稱到客戶端

  27. socket.write_some(boost::asio::buffer("hello CSND_Ayo"));

  28. // 阻塞接收客戶端發來的資料

  29. socket.read_some(boost::asio::buffer(msg));

  30. // 列印客戶端發來的資料

  31. std::cout << "client reply: " << msg.c_str() << std::endl;

  32. }

  33. }

  34. catch (std::exception& _e) {

  35. std::cout << "server exceptional." << std::endl;

  36. std::cout << _e.what() << std::endl;

  37. }

  38. std::cout << "server end." << std::endl;

  39. getchar();

  40. return 0;

  41. }

非同步(Async)

處理客戶端的請求時,不會因為客戶端的延遲響應而導致程式假死

  1. #include <iostream>

  2. #include <boost/asio.hpp>

  3. #include <boost/bind.hpp>

  4. // 非同步伺服器類

  5. class Server {

  6. private:

  7. // 服務例項

  8. boost::asio::io_service& ios_;

  9. // 接收器例項

  10. boost::asio::ip::tcp::acceptor acceptor_;

  11. // socket智慧指標

  12. typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;

  13. public:

  14. Server(boost::asio::io_service& _ios) : ios_(_ios),

  15. acceptor_(_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 13695)) {

  16. // 預設執行

  17. start();

  18. }

  19. // 啟動網路偵聽的操作入口

  20. void start(void) {

  21. // 自定義的智慧指標

  22. socket_ptr socket(new boost::asio::ip::tcp::socket(ios_));

  23. // 非同步偵聽,若有服務連線,則自動呼叫Server::handler_accept函式,並將error, socket傳入作為引數

  24. acceptor_.async_accept(*socket,

  25. boost::bind(&Server::accept_handler, this,

  26. boost::asio::placeholders::error/* 此處作為佔位符 */, socket));

  27. }

  28. // 請求者響應後觸發的處理器

  29. void accept_handler(const boost::system::error_code& _ec, socket_ptr _socket) {

  30. // 錯誤碼檢測

  31. if (_ec) {

  32. return;

  33. }

  34. // 列印當前連線進來的客戶端

  35. std::cout << "client: " << _socket->remote_endpoint().address() << std::endl;

  36. // 非同步傳送 "hello CSND_Ayo" 訊息到客戶端,傳送成功後,自動呼叫Server::write_handler函式

  37. _socket->async_write_some(boost::asio::buffer("hello CSND_Ayo"),

  38. boost::bind(&Server::write_handler, this,

  39. boost::asio::placeholders::error/* 此處作為佔位符 */));

  40. // 啟動新的非同步監聽

  41. start();

  42. }

  43. // 完成非同步寫操作後的處理器

  44. void write_handler(const boost::system::error_code& _ec) {

  45. std::cout << "server: send message complete." << std::endl;

  46. }

  47. };

  48. int main(void) {

  49. try {

  50. std::cout << "server start." << std::endl;

  51. // 建造服務物件

  52. boost::asio::io_service ios;

  53. // 構建Server例項

  54. Server server(ios);

  55. // 啟動非同步呼叫事件處理迴圈

  56. ios.run();

  57. }

  58. catch (std::exception& _e) {

  59. std::cout << _e.what() << std::endl;

  60. }

  61. std::cout << "server end." << std::endl;

  62. return 0;

  63. }

作者的簡易併發伺服器類

使用兩個類來撰寫了一個併發的伺服器類 Server(伺服器監聽類)、Session(會話類)

具備功能:

1、非同步監聽客戶端連線

2、客戶連線時,首包要求具有特定格式(協議包)

3、併發處理客戶端互動

當前類的網路互動協議拓撲圖

Server.h

  1. #ifndef __CLY_SERVER_H__

  2. #define __CLY_SERVER_H__

  3. #include <string.h>

  4. #include <boost/asio.hpp>

  5. #include <boost/shared_ptr.hpp>

  6. class Session;

  7. class Server {

  8. private:

  9. // 會話 - 智慧指標

  10. typedef boost::shared_ptr<Session> session_ptr;

  11. public:

  12. Server(boost::asio::io_service &_ioService, boost::asio::ip::tcp::endpoint &_endpoint);

  13. virtual ~Server(void);

  14. // 監聽

  15. void start(void);

  16. // 非同步

  17. void run(void);

  18. private:

  19. // 資料匯出介面

  20. void callback_session(std::string _fromIp, std::string _info);

  21. // 會話啟動

  22. void accept_handler(session_ptr _chatSession, const boost::system::error_code& _error);

  23. private:

  24. boost::asio::io_service &ioService_;

  25. boost::asio::ip::tcp::acceptor acceptor_;

  26. };

  27. #endif // __CLY_SERVER_H__

Server.cpp

  1. #include <boost/bind.hpp>

  2. #include "Server.h"

  3. #include "Session.h"

  4. Server::Server(boost::asio::io_service &_ioService, boost::asio::ip::tcp::endpoint &_endpoint)

  5. : ioService_(_ioService), acceptor_(_ioService, _endpoint) {

  6. start();

  7. }

  8. Server::~Server(void) {

  9. }

  10. void Server::start(void) {

  11. session_ptr new_chat_session(new Session(ioService_));

  12. acceptor_.async_accept(new_chat_session->socket(),

  13. boost::bind(&Server::accept_handler, this, new_chat_session,

  14. boost::asio::placeholders::error));

  15. }

  16. void Server::run(void) {

  17. ioService_.run();

  18. }

  19. void Server::callback_session(std::string /*_fromIp*/, std::string /*_info*/) {

  20. return;

  21. }

  22. void Server::accept_handler(session_ptr _chatSession, const boost::system::error_code& _error) {

  23. if (!_error && _chatSession) {

  24. try {

  25. _chatSession->start();

  26. start();

  27. }

  28. catch (...) {

  29. return;

  30. }

  31. }

  32. }

Session.h

  1. #ifndef __CLY_SESSION_H__

  2. #define __CLY_SESSION_H__

  3. #include <iostream>

  4. #include <string>

  5. #include <boost/asio.hpp>

  6. #include <boost/enable_shared_from_this.hpp>

  7. #define REPLY_SIZE (32)

  8. // 會話類

  9. class Session : public boost::enable_shared_from_this<Session>

  10. {

  11. public:

  12. typedef void pSessionCallback(std::string, std::string);

  13. public:

  14. Session(boost::asio::io_service& _ioService);

  15. virtual ~Session(void);

  16. void start(void);

  17. void setCallback(pSessionCallback* _callback) { callback_ = _callback; }

  18. // socket 例項

  19. boost::asio::ip::tcp::socket& socket(void);

  20. private:

  21. // 第一個協議包

  22. void init_handler(const boost::system::error_code& _error);

  23. // 解析協議包

  24. void analyse_handler(const boost::system::error_code& _error);

  25. // 完成資料傳輸後觸發的收尾工作

  26. void done_handler(const boost::system::error_code& _error);

  27. // 讀取成功後觸發的函式

  28. void read_handler(const boost::system::error_code& _error, size_t _readSize);

  29. // 寫入完成後觸發的函式

  30. void write_handler(const boost::system::error_code& _error);

  31. private:

  32. // 臨時資訊緩衝區

  33. char msg_[1024];

  34. std::string currentMsg_;

  35. // 資料總數量

  36. int sumSize_;

  37. // 單個數據包大小

  38. unsigned int maxSize_;

  39. // socket控制代碼

  40. boost::asio::ip::tcp::socket socket_;

  41. // 回撥

  42. pSessionCallback* callback_;

  43. };

  44. #endif // __CLY_SESSION_H__

Session.cpp

  1. #include <boost/bind.hpp>

  2. #include "Session.h"

  3. Session::Session(boost::asio::io_service& _ioService)

  4. :socket_(_ioService) {

  5. memset(msg_, 0, sizeof(msg_));

  6. }

  7. Session::~Session(void)

  8. {

  9. }

  10. void Session::start(void) {

  11. // 告訴連結成功的客戶端,你想要的資訊。

  12. char msg[256] = "001:Connect Succeed! Please tell me with 10 bytes, the total data and the size of each package, example:128 1024";

  13. boost::asio::async_write(socket_, boost::asio::buffer(msg, strlen(msg)),

  14. boost::bind(&Session::init_handler, shared_from_this(),

  15. boost::asio::placeholders::error));

  16. }

  17. boost::asio::ip::tcp::socket& Session::socket(void) {

  18. return socket_;

  19. }

  20. // 第一個協議包

  21. void Session::init_handler(const boost::system::error_code& _error) {

  22. if (_error) {

  23. return;

  24. }

  25. // 讀取客戶端發來的 10 bytes,確定單個包的大小以及資料總大小

  26. boost::asio::async_read(socket_, boost::asio::buffer(msg_, 10),

  27. boost::bind(&Session::analyse_handler, shared_from_this(),

  28. boost::asio::placeholders::error));

  29. }

  30. void Session::analyse_handler(const boost::system::error_code& _error) {

  31. if (_error) {

  32. return;

  33. }

  34. // 分析協議包格式

  35. bool bflag = true;

  36. // 正則分析格式

  37. // do something.

  38. if (!bflag) {

  39. start();

  40. return;

  41. }

  42. // 格式化儲存協議包資料

  43. std::stringstream io(msg_);

  44. io >> maxSize_;

  45. io >> sumSize_;

  46. // 傳送接收請求資訊

  47. char msg[REPLY_SIZE];

  48. sprintf_s(msg, "001:is ok, data remaining %d.", sumSize_);

  49. boost::asio::async_write(socket_, boost::asio::buffer(msg, REPLY_SIZE),

  50. boost::bind(&Session::write_handler, shared_from_this(),

  51. boost::asio::placeholders::error));

  52. }

  53. // 完成資料傳輸

  54. void Session::done_handler(const boost::system::error_code& _error) {

  55. if (_error) {

  56. return;

  57. }

  58. currentMsg_ += msg_;

  59. // 傳送資訊到回撥

  60. if (!currentMsg_.empty() && callback_ != nullptr) {

  61. callback_(socket_.remote_endpoint().address().to_string(), currentMsg_);

  62. currentMsg_.clear();

  63. }

  64. memset(msg_, 0, sizeof(msg_));

  65. char msg[32] = "001:will done.";

  66. boost::asio::async_write(socket_, boost::asio::buffer(msg, REPLY_SIZE),

  67. boost::bind(&Session::init_handler, shared_from_this(),

  68. boost::asio::placeholders::error));

  69. }

  70. void Session::read_handler(const boost::system::error_code& _error, size_t _readSize) {

  71. if (_error) {

  72. return;

  73. }

  74. // 資料處理

  75. currentMsg_ += msg_;

  76. if (currentMsg_.size() > 1024 * 512) {

  77. // 傳送資訊到回撥

  78. if (callback_ != nullptr) {

  79. callback_(socket_.remote_endpoint().address().to_string(), currentMsg_);

  80. currentMsg_.clear();

  81. }

  82. }

  83. memset(msg_, 0, sizeof(msg_));

  84. // 計算當前剩餘資料數量

  85. sumSize_ -= _readSize;

  86. // 接收完成

  87. if (0 > sumSize_) {

  88. done_handler(_error);

  89. }

  90. // 繼續接收

  91. else {

  92. char msg[REPLY_SIZE];

  93. sprintf_s(msg, "001:%d.", sumSize_);

  94. boost::asio::async_write(socket_, boost::asio::buffer(msg, REPLY_SIZE),

  95. boost::bind(&Session::write_handler, shared_from_this(),

  96. boost::asio::placeholders::error));

  97. std::cout << "send client recv succeed: " << msg << std::endl;

  98. }

  99. }

  100. void Session::write_handler(const boost::system::error_code& _error) {

  101. if (_error) {

  102. return;

  103. }

  104. boost::asio::async_read(socket_, boost::asio::buffer(msg_, maxSize_),

  105. boost::bind(&Session::read_handler, shared_from_this(),

  106. boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

  107. }

main.cpp

  1. #include <iostream>

  2. #include <boost/asio.hpp>

  3. #include <boost/bind.hpp>

  4. #include "Server.h"

  5. int main(void) {

  6. try {

  7. std::cout << "server start." << std::endl;

  8. // 建造服務物件

  9. boost::asio::io_service ios;

  10. // 具體的伺服器地址與埠

  11. boost::asio::ip::tcp::endpoint endpotion(boost::asio::ip::tcp::v4(), 13695);

  12. // 構建Server例項

  13. Server server(ios, endpotion);

  14. // 啟動非同步呼叫事件處理迴圈

  15. server.run();

  16. }

  17. catch (std::exception& _e) {

  18. std::cout << _e.what() << std::endl;

  19. }

  20. std::cout << "server end." << std::endl;

  21. return 0;

  22. }