C/C++利用Boost::Asio網路庫建立自己的Socket伺服器
引言
寸光陰,當下我們或許更需要利用現有的知識,應用現有的技術。網路是當前網際網路的根本,瞭解網路便開始顯得極其重要。今天我們利用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;
}
相關文章
- 一個C++ boost非同步socket serverC++非同步Server
- Boost.Asio和ACE之間關於Socket程式設計的比較程式設計
- Ubuntu下安裝C++ boost庫UbuntuC++
- C++跨平臺庫boost和Poco的編譯C++編譯
- Signals-The Boost C++ LibrariesC++
- linux下使用boost.python呼叫c++動態庫LinuxPythonC++
- socket程式設計實現tcp伺服器_C/C++程式設計TCP伺服器C++
- 簡單解析C++基於Boost庫實現命令列C++命令列
- C++使用Boost多執行緒C++執行緒
- 超越C++標準庫:Boost庫導論電子書PDF下載C++
- C++霧中風景12:聊聊C++中的Mutex,以及拯救生產力的BoostC++Mutex
- C++ 高效能伺服器網路框架設計細節C++伺服器框架
- Mudo C++網路庫第七章學習筆記C++筆記
- Mudo C++網路庫第十一章學習筆記C++筆記
- Mudo C++網路庫第五章學習筆記C++筆記
- 如何建立自己的快速、私有的開源網狀網路(mesh)
- C++中簡單使用HP-SocketC++
- 如何用C++自己實現mysql資料庫的連線池?C++MySql資料庫
- Socket網路程式設計基礎與實踐:建立高效的網路通訊程式設計
- 使用Boost庫報error C4996錯誤Error996
- Mudo C++網路庫第六章學習筆記C++筆記
- Mudo C++網路庫第八章學習筆記C++筆記
- Mudo C++網路庫第十章學習筆記C++筆記
- C# 使用Fluent API 建立自己的DSLC#API
- socket通訊的建立
- C++標準庫、C++標準模版庫介紹C++
- C++中有三種建立物件的方法C++物件
- C++ Qt開發:QNetworkInterface網路介面元件C++QT元件
- C++ Qt開發:QNetworkAccessManager網路介面元件C++QTSSM元件
- 《Effective C++》第三版-1. 讓自己習慣C++(Accustoming Yourself to C++)C++
- flutter中呼叫C++的庫FlutterC++
- buck電路 & boost電路
- 利用WordPress搭建屬於自己的網站網站
- Linux Socket C語言網路程式設計:TCP SocketLinuxC語言程式設計TCP
- Linux Socket C語言網路程式設計:UDP SocketLinuxC語言程式設計UDP
- Linux Socket C語言網路程式設計:Select SocketLinuxC語言程式設計
- c/c++ 標準庫 vectorC++
- Windows10 VS2017 C++ Server Socket簡單伺服器端與客戶端WindowsC++Server伺服器客戶端