基于BOOST 实现并发服务器框架
一:设计思路
本服务器框架使用 UDP 传输协议,程序柱线程等待客户端数据,并将数组存取队列缓冲区。另外可开启多个工作线程,工作线程可以依据具体项目实现不同的功能 ,例如可以将队列缓冲区中的数据逐个取出存入数据库,本程序为说明方便只是将缓冲区中的数据逐个打印输出。
二:代码示例
1. 队列缓冲区实现
队列缓冲区由 unordered_map 和 queue 共同实现,unordered_map 散列表中每个元素 key 设置成客户端的ip value 设置成该ip 地址传过来数据的队列。
//file name: exclusive_queue.h #ifndef __EXCLUSIVE_QUEUE_H__
#define __EXCLUSIVE_QUEUE_H__ #include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <boost/unordered/unordered_map.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/assert.hpp>
#include <queue>
#include <string> #define BOOST_DATE_TIME_SOURCE
#define BOOST_THREAD_NO_LIB using namespace std;
using namespace boost; namespace proxy
{ template <typename T>
class exclusive_queue
{
private : typedef mutex mutex_t; class QueueState
{
public :
typedef shared_ptr<queue<T> > queue_t;
mutex_t m_mu_queue;
short m_busy;
queue_t m_queue; QueueState() : m_busy(), m_queue(new queue<T>())
{
} }; typedef shared_ptr<QueueState > queue_state_t;
typedef shared_ptr<unordered_map<string, queue_state_t> > unordered_map_t; unsigned int m_queue_size;
unordered_map_t m_queues;
queue_state_t m_cur_que;
mutex_t m_mu_queues; public : exclusive_queue(int cnt = ) : m_queue_size(cnt)
,m_queues(new unordered_map<string, queue_state_t>())
{ } void Enqueue(string key, T element)
{
{
mutex_t::scoped_lock lock(m_mu_queues);
if(!m_queues->count(key))
{
m_queues->insert(make_pair(key, make_shared<QueueState>()));
}
m_cur_que = (*m_queues)[key];
} {
mutex_t::scoped_lock lock(m_cur_que->m_mu_queue);
if((*m_cur_que).m_queue->size() >= m_queue_size)
{
cout<<"queue is full "<< key <<endl;
}
else
{
(*m_cur_que).m_queue->push(element);
}
} } void Dequeue(queue<T>& result, string& key)
{
queue_state_t cur_qs;
BOOST_ASSERT(cur_qs == NULL);
while(cur_qs == NULL)
{
mutex_t::scoped_lock lock(m_mu_queues);
BOOST_AUTO(it, m_queues->begin());
for(; it != m_queues->end(); ++it)
{
if((*it).second->m_busy == && (*it).second->m_queue->size() > )
{
key = (*it).first;
cur_qs = (*it).second;
cur_qs->m_busy = ;
break;
}
} }
BOOST_ASSERT(cur_qs != NULL);
{
if(cur_qs->m_busy == )
{
mutex_t::scoped_lock lock(cur_qs->m_mu_queue);
while(!cur_qs->m_queue->empty())
{
result.push(cur_qs->m_queue->front());
cur_qs->m_queue->pop();
}
BOOST_ASSERT(cur_qs->m_queue->size() == );
}
} } void Release(string key)
{
{
mutex_t::scoped_lock lock(m_mu_queues);
(*m_queues)[key]->m_busy = ;
} } }; } #endif
2 服务器端实现
服务器主线程首先开启若干个工作线程, 工作线程循环读取数据缓冲区内容,当数据缓冲器有数据则取出 打印输出。
//file name: server.h #ifndef __SERVER_H__
#define __SERVER_H__ #define BOOST_THREAD_NO_LIB
#include <boost/thread.hpp> #define BOOST_REGEX_NO_LIB
#define BOOST_DATE_TIME_SOURCE
#define BOOST_SYSTEM_NO_LIB
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp> #include <ostream> #include "configurationmanager.h"
#include "datadump.h"
#include "exclusive_queue.h" using namespace boost::asio;
using namespace std;
namespace proxy
{ class server
{
typedef shared_ptr<ip::udp::socket> udp_socket_t;
private :
io_service m_ios;
udp_socket_t m_socket;
ip::udp::endpoint m_remote_ep;
system::error_code m_ec;
proxy::exclusive_queue<DataMessage> m_data_queue; private : void post_async(int id)
{
queue<DataMessage> data_queue;
cout<<"[Thread]:"<<id<<"start"<<endl;
string logstr;
string key;
while()
{
m_data_queue.Dequeue(data_queue, key);
if(data_queue.size() == )
continue;
cout<<"[thread "<<id<<"]"<<endl;
DataDump::Dump(data_queue);
m_data_queue.Release(key);
}
} void server_start()
{
cout<<"Server start..."<<endl;
DataMessage msg;
ip::udp::endpoint ep;
system::error_code ec;
while(true)
{
m_socket->receive_from(buffer(msg.m_data), m_remote_ep, , m_ec); if(ec && ec != error::message_size)
{
throw system::system_error(ec);
}
msg.m_ip = ep.address().to_string();
m_data_queue.Enqueue(ep.address().to_string(), msg);
}
} public : server()
{
int i;
int work_thread_num = ConfigurationManager<long>::AppSetting("conf.WorkThreadNum");
int port = ConfigurationManager<long>::AppSetting("conf.ListenningPort");
ip::udp::endpoint local_ep(ip::udp::v4(), port);
m_socket = udp_socket_t(new ip::udp::socket(m_ios, local_ep));
for(i = ; i < work_thread_num; ++i)
{
thread t(boost::bind(&server::post_async, this, i));
}
server_start();
} }; } #endif
3 数据协议格式
本程序协议格式只存在两个字段 ,客户端的 ip 与 数据,结构参考代码
//file name : datamessage.h #ifndef __DATAMESSAGE_H__
#define __DATAMESSAGE_H__
#include <string> namespace proxy
{ class DataMessage
{ public :
std::string m_ip;
char m_data[];
}; } #endif
4 工作线程
本程序可依据不同的项目具体实现,本程序为说明简单,只是单纯的将工作线程取到的数据全部打印输出
//file name: datadump.h #ifndef __DATADUMP_H__
#define __DATADUMP_H__ #include <ostream>
#include <queue>
#include <string> #include "datamessage.h"
using namespace std;
using namespace boost; namespace proxy
{ class DataDump
{ public :
static void Dump(queue<DataMessage> &queue)
{
DataMessage msg;
while(queue.size() != )
{
msg = queue.front();
queue.pop();
cout<<"[dump] "<<msg.m_ip<<" : "<<&(msg.m_data[]) <<endl;
}
}
}; } #endif
5 读取配置文件
本程序只是简单讲boost 库中的 propertytree 库进行了简单封装,想参考Boost 详细说明
//file name : configurationmanager.h #ifndef __CONFIGURATIONMANAGER_H__
#define __CONFIGURATIONMANAGER_H__ #include <ostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/assign.hpp>
#include <boost/typeof//typeof.hpp> using namespace std;
using namespace boost::property_tree; namespace proxy
{ template <class T>
class ConfigurationManager
{
private : public :
static T AppSetting(string path)
{
ptree pt;
read_xml("conf.xml", pt); return pt.get<T>(path);
}
}; } #endif
6 配置文件
使用 xml 格式,存放服务器监听端口与工作线程的数量
<?xml version="1.0" encoding="utf-8"?>
<conf>
<ListenningPort></ListenningPort>
<WorkThreadNum></WorkThreadNum>
</conf>
三 主要技术说明
主要基于boost 库多线程,互斥锁,网络传输套接字,重点是实现一个线程安全型的数据队列。程序结构非常简单
g++ *.cpp -o main -Wall -lboost_thread -lboost_system
基于BOOST 实现并发服务器框架的更多相关文章
- 基于Golang的游戏服务器框架cellnet开发日记(二)
看官们肯定还有大部分不是很熟悉Actor模型. 我这里基于Erlang, Skynet等语言和框架库来实战型解释下Actor模型. Actor概念 Actor模型和OO类似, 都是符合人的思维模式进 ...
- 基于EasyDarwin开源流媒体服务器框架实现EasyNVR H5无插件直播流媒体服务器方案
背景分析 在之前的一篇博客<web无插件播放RTSP摄像机方案,拒绝插件,拥抱H5!>中,描述了实现一套H5无插件直播方案的各个组件的参考建议,又在博客<EasyNVR H5流媒体服 ...
- PHP写的异步高并发服务器,基于libevent
PHP写的异步高并发服务器,基于libevent 博客分类: PHP PHPFPSocketLinuxQQ 本文章于2013年11月修改. swoole已使用C重写作为PHP扩展来运行.项目地址:h ...
- 跨平台网络通信与服务器框架 acl 3.2.0 发布,acl_cpp 是基于 acl 库的 C++ 库
acl 3.2.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/F ...
- 基于线程池、消息队列和epoll模型实现并发服务器架构
引言 并发是什么?企业在进行产品开发过程中为什么需要考虑这个问题?想象一下天猫的双11和京东的618活动,一秒的点击量就有几十万甚至上百万,这么多请求一下子涌入到服务器,服务器需要对这么多的请求逐个进 ...
- RedRabbit——基于BrokerPattern服务器框架
RedRabbit 经典网游服务器架构 该图省略了专门用途的dbserver.guildserver等用于专门功能的server,该架构的优点有: l LoginGate相当于DNS,可以动态的保证G ...
- 基于BrokerPattern服务器框架
基于BrokerPattern服务器框架 RedRabbit 经典网游服务器架构 该图省略了专门用途的dbserver.guildserver等用于专门功能的server,该架构的优点有: l Log ...
- 并发服务器--02(基于I/O复用——运用Select函数)
I/O模型 Unix/Linux下有5中可用的I/O模型: 阻塞式I/O 非阻塞式I/O I/O复用(select.poll.epoll和pselect) 信号驱动式I/O(SIGIO) 异步I/O( ...
- 基于Netty打造RPC服务器设计经验谈
自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...
随机推荐
- 【转】在XCode工程中创建bundle包
http://www.giser.net/?p=859 Bundle Programming Guide: https://developer.apple.com/library/ios/docume ...
- Mechanism of Loading Resources
Mechanism of Loading Resources 1. Distributed strategy 1.1. Developer guilde 1.2. Notes 2. Centraliz ...
- php安装ecshop
1.apache2.2正常 2.源码htdoc下面保存不动 3.E:\PHP 安装目录下 php5.3.28 就不支持jpeg php5.2.17 就不支持 mysql 说明就是php的问题,因为其他 ...
- oracle的sql函数
只读事务set transaction read only当一个用户添加了只读事务,则查询时只会查到设置只读事务之前的内容,在并发量大的系统中,通过设置只读事务 便于统计 oracle的sql函数的使 ...
- MySql安装步骤详解,MySql的root密码设置,启动MySql服务。
1.下载mysql安装包,并解压,双击mysql-5.6.24-winx64.msi 2.点击下一步 3.选择custom 4.选择安装内容和位置,5个安装内容要选择will be installed ...
- svn服务器的搭建
subversion是优秀的版本管理工具,下面简单介绍svn服务器和客户端的下载.安装.搭建以及使用 一:下载svn服务器和客户端 1:下载地址服务器下载地址:http://subversion.ap ...
- 关于Hibernate的总结
主键的生成策略: * increment :是由Hibernate完成的自动增长.(不是数据库底层的自动增长的方式.) * select max(id) from customer * ...
- 创建eclipse新的workspace并设置workspace共享配置
一:创建新的workspace 1.File——Switch Workspace——Other 2.修改workspace路径和名称 3.修改后如下: 4.点击OK按钮后,eclipse自动重启 同时 ...
- 记工作中的git遇到的问题
看了 git 回退到某版本后,再在此版本上更新,无法push 操作前,我备份了修改了目录,准备建一个分支进行操作 我在本地revert了一次,commit到了远程仓库.然后上个版本的修改给恢复了... ...
- beanfactory与applicationcontext的区别
ApplicationContext能够自动辨认和应用在其上部署的实现了BeanFactoryPostProcessor的bean 特性 Bean ...