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;
}
时间: 04-23

C/C++利用Boost::Asio网络库建立自己的Socket服务器的相关文章

boost中asio网络库多线程并发处理实现,以及asio在多线程模型中线程的调度情况和线程安全。

1.实现多线程方法: 其实就是多个线程同时调用io_service::run for (int i = 0; i != m_nThreads; ++i)        {            boost::shared_ptr<boost::thread> pTh(new boost::thread(                boost::bind(&boost::asio::io_service::run,&m_ioService)));            m_l

Boost Asio 网络编程 基本用法

Boost Asio 网络编程 基本用法 flyfish 2015-2-9 IP地址 boost::asio::ip::address表示IP地址,同时支持ipv4和ipv6. boost::asio::ip::address addr; addr = addr.from_string("127.0.0.1"); assert(addr.is_v4()); OutputDebugStringA(addr.to_string().c_str()); addr = addr.from_st

boost::asio网络传输错误码的一些实验结果(recv error_code)

错误码很重要,可以由此判断网络连接到底发生了神马事情,从而驱动高层逻辑的行为.只有笼统的错误码判断的网络层是不够规范的,鄙人觉得有些错误码还是需要在网络层就区分开的,特此记录一些当前实验的错误码以及发生原因. 以下是一部分在async_receive()的handler处捕获到的比较有用的错误码 错误码(十进制) 枚举 发现原因 10009 boost::asio::error::bad_descriptor 在一个已经关闭了的套接字上执行async_receive() 995 boost::a

boost Asio网络编程简介

.markdown-preview:not([data-use-github-style]) { padding: 2em; font-size: 1.2em; color: rgb(171, 178, 191); background-color: rgb(40, 44, 52); overflow: auto } .markdown-preview:not([data-use-github-style])>:first-child { margin-top: 0px } .markdown-

boost asio 学习(九) boost::asio 网络封装

http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio?pg=10 9. A boost::asio network wrapper (TCP) 现在我们了解asio和TCP网络方面的知识,我们可以尝试下封装网络底层.通过使用这个封装,我们可以重用代码并且将精力集中于业务逻 辑方面而不在网络通讯方面花费太多精力. 重要提示:本代码仅仅用于教学目的.不要在商业系统中使用该代码,

boost asio 网络聊天 代码修改学习

简化asio的聊天代码 去除ROOM的设计 所有连接客户端均在同一个ROOM下 chat message 使用boost自带示例的头文件 #pragma once #include <cstdio> #include <cstdlib> #include <cstring> class chat_message { public: enum { header_length = 4 }; enum { max_body_length = 512 }; chat_messa

boost.ASIO-可能是下一代C++标准的网络库

曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的一个,也有像ACE这样重量级的网络框架.去年,Boost将ASIO纳入了自己的体系,由于Boost的影响力,ASIO有机会成为标准网络库.作者Chris Kohlhoff以ASIO为样本向C++标准委员会提交了一个网络库建议书,里面提到:ASIO的覆盖范围: Networking using TC

10 C++ Boost ASIO网路通信库 TCP/UDP,HTTP

  tcp 同步服务器,显示服务器端时间 tcp 同步服务器,提供多种选择 多线程的tcp 同步服务器 tcp 同步客户端 boost 域名地址解析 tcp异步服务器 tcp 异步客户端 UDP同步服务器 UDP同步客户端 UDP异步服务器 UDP异步客户端 HTTP同步客户端 HTTP异步客户端 同步实验: 异步实验 多线程异步实验 tcp 同步服务器,显示服务器端时间 [email protected]:~/boost$ cat main.cpp  #include <ctime> #in

Boost.Asio技术文档

Christopher Kohlhoff Copyright ? 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE_1_0.txt文件或从http://www.boost.org/LICENSE_1_0.txt) Boost.Asio是用于网络和低层IO编程的跨平台C++库,为开发者提供了C++环境下稳定的异步模型. 综述 基本原理 应用程序与外界交互的方式有很多,可通过文件,网络,串口或控制台.例如在网络通信中,完