一:设计思路

  本服务器框架使用 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 实现并发服务器框架的更多相关文章

  1. 基于Golang的游戏服务器框架cellnet开发日记(二)

    看官们肯定还有大部分不是很熟悉Actor模型. 我这里基于Erlang, Skynet等语言和框架库来实战型解释下Actor模型.  Actor概念 Actor模型和OO类似, 都是符合人的思维模式进 ...

  2. 基于EasyDarwin开源流媒体服务器框架实现EasyNVR H5无插件直播流媒体服务器方案

    背景分析 在之前的一篇博客<web无插件播放RTSP摄像机方案,拒绝插件,拥抱H5!>中,描述了实现一套H5无插件直播方案的各个组件的参考建议,又在博客<EasyNVR H5流媒体服 ...

  3. PHP写的异步高并发服务器,基于libevent

    PHP写的异步高并发服务器,基于libevent 博客分类: PHP PHPFPSocketLinuxQQ  本文章于2013年11月修改. swoole已使用C重写作为PHP扩展来运行.项目地址:h ...

  4. 跨平台网络通信与服务器框架 acl 3.2.0 发布,acl_cpp 是基于 acl 库的 C++ 库

    acl 3.2.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/F ...

  5. 基于线程池、消息队列和epoll模型实现并发服务器架构

    引言 并发是什么?企业在进行产品开发过程中为什么需要考虑这个问题?想象一下天猫的双11和京东的618活动,一秒的点击量就有几十万甚至上百万,这么多请求一下子涌入到服务器,服务器需要对这么多的请求逐个进 ...

  6. RedRabbit——基于BrokerPattern服务器框架

    RedRabbit 经典网游服务器架构 该图省略了专门用途的dbserver.guildserver等用于专门功能的server,该架构的优点有: l LoginGate相当于DNS,可以动态的保证G ...

  7. 基于BrokerPattern服务器框架

    基于BrokerPattern服务器框架 RedRabbit 经典网游服务器架构 该图省略了专门用途的dbserver.guildserver等用于专门功能的server,该架构的优点有: l Log ...

  8. 并发服务器--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( ...

  9. 基于Netty打造RPC服务器设计经验谈

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

随机推荐

  1. Maven学习小结(二 项目构建过程)

    1.创建Maven项目 1.1 创建Maven项目的约定目录结构 1.2 编辑pom.xml <project xmlns="http://maven.apache.org/POM/4 ...

  2. 如何使用MVP模式搭建我们的Android应用?

    听到很多人在讨论MVVM,我自己早些时候也写过一篇介绍MVVM的文章(玩转Android之MVVM开发模式实战,炫酷的DataBinding!),实际上,在Android开发领域中,除了MVVM之外, ...

  3. Java基础知识强化之网络编程笔记20:Android网络通信之 Android常用OAuth登录和分享

    1.  申请百度开发者账号及百度OAuth简介. (1)申请开发者账号: http://developer.baidu.com/ (2)创建项目: http://developer.baidu.com ...

  4. Azure CLI (一)如何安装和配置Azure CLI

    什么是Azure CLI 快速安装 Azure 命令行界面 (Azure CLI),以便使用一组基于 shell 的开源命令在 Azure 中创建和管理资源. 步骤 1:安装 . 登录https:// ...

  5. 初识 Lucene

    Lucene是一个信息检索工具库,而不是一个完整的搜索程序 搜索程序 Lucene索引核心类 Lucene索引核心类: Document: 文档对象代表一些域(field)的集合 Field: 每个文 ...

  6. PHP之ThinkPHP数据操作CURD

    两个数据表 具体操作如下: /**********************数据库操作********************/       ////////添加数据//////////         ...

  7. Javascript实现图片无缝滚动

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. Spring 事务模型

    一.三种事务模型 1.本地事务模型:开发人员不用知道事务的存在,事务全部交给数据库来管理,数据库自己决定什么时候提交或回滚,所以数据库是事务的管理者. Connection conn=jdbcDao. ...

  9. JavaScript检测实例属性, 原型属性

    0.前提 JavaScript对象的属性分为两种存在形态. 一种是存在实例中, 另一是存在原型对象中. 根据上述, 检测属性的时候会出现4种情况 既不存在实例中, 也不存在原型对象中 存在实例中, 不 ...

  10. Python(2.7.6) 标准日志模块 - Logging Handler

    Python 标准日志模块使用 Handler 控制日志消息写到不同的目的地,如文件.流.邮件.socket 等.除了StreamHandler. FileHandler 和 NullHandler ...