寸光阴,当下我们或许更需要利用现有的知识,应用现有的技术。网络是当前互联网的根本,了解网络便开始显得极其重要。今天我们利用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、异步:处理客户端的请求时,不会因为客户端的延迟响应而导致程序假死
电脑里有非常多的端口,而客户端只会把消息传到约定的地址与端口,只有在正确的端口等待,才能接到自己预期的客户。
就好像楼房里有非常多层楼一样,而快递员只会把物品送到约定的楼层,只有在正确的楼层等待,才能达成预期的结果。
#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; }
一个基本的连接,在正常的情况下,应该由客户端发起,服务器应该处于实时监听的状态,因为能接到客户端发起的连接请求,这才是网络操作的根本。
#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; }
一旦服务器收到客户端发起的连接请求,便为客户端建立服务。与客户端建立连接的目的,始终是为了交互,我们不能本末倒置。
我们尝试一下,第一次交互的滋味。
#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; }
处理客户端的请求时,不会因为客户端的延迟响应而导致程序假死
#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; }
本文出自 “12620900” 博客,请务必保留此出处http://12630900.blog.51cto.com/12620900/1918548
C/C++利用Boost::Asio网络库建立自己的Socket服务器
原文地址:http://12630900.blog.51cto.com/12620900/1918548