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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章