一个高在线(可以超过1024)多线程的socket echo server(pthreads 和 libevent扩展)
研究了3周吧,本来打算用pthreads+event扩展的,结果event扩展太原始了,太多函数了,实在不知道怎么在外部随时发送数据给客户端,所以改用libevent,
改用libevent之后花了2个小时就运行起来了。
当然并不敢说稳定,而且有个地方用了一个“适应”bug的地方,避免bug
两个扩展都从pecl.php.net下载就可以了,
安装,不想写了,16:25了还没吃早饭 午饭
上代码,欢迎讨论:
我的QQ群:
PHPer&Webgame&移动开发,群号:95303036
<?php
class statWorker extends Worker {
public function __construct() { }
public function run(){
while(!$this->isShutdown()){
sleep(3);
}
}
}
class sendAllWorker extends Worker {
public function __construct(&$_logicWorker) {
$this->logicWorker = $_logicWorker;
}
public function run(){ }
}
class data extends Stackable{
public function __construct() {
echo __FILE__.'-'.__LINE__.'<br/>'.chr(10);
}
public function run(){
echo __FILE__.'-'.__LINE__.'<br/>'.chr(10);
}
}
class logicWorker extends Worker{
public $data;
public function __construct(&$_data) {
echo __FILE__.'-'.__LINE__.'<br/>'.chr(10);
$this->data = $_data;
}
public function run(){
$data = $this->data;
$count=0;
while(1){
if($arr =(array) $this->shift()){
echo __FILE__.'-'.__LINE__.'<br/>'.chr(10);
if(!$arr) continue;
if(isset($arr[0])){
echo $arr[0].' from '.$arr[1].chr(10);
//$data[] = array('You say:'.$arr[0], $arr[1]%2==0?0:$arr[1]);
$data[] = array('You say:'.$arr[0], 0);
} if((++$count)%1000==0){
printf("Work Mermory used %.3fMB RAM, time: %3f===> %d \n",
memory_get_peak_usage(true)/1048576, (microtime(true) - $stime), $count);
$stime = microtime(true);
}
}else usleep(100000);
}
}
}
class listenerWorkerRunner extends Worker {
public function __construct(&$_listener){
$this->listener = $_listener;
} public function run(){
$this->listener->dispatch();
}
}
class senderWorker extends Worker {
public function __construct(&$_data, &$clients) {
$this->data = $_data;
$this->clients = $clients;
}
public function run(){
echo __FILE__.'-'.__LINE__.'<br/>'.chr(10);
$data = $this->data;
$clients = $this->clients;
while(1)
{
$arr= $data->shift();
if($arr){
$id = $arr[1];
$msg = $arr[0];
echo '===================================senderWorker GOT: '.trim($msg).' from '.$id.chr(10);
//var_dump($clients);
if(is_array($id))
foreach($id as $i)
fwrite($clients[$i], $msg);
else if($id===0){
//必须这样,否则会报错,$clients的最后一个成员会变成 resource(2) of type (unknown)
$_clients = (array) $clients;
var_dump($_clients);
foreach($_clients as $i=>$c)
fwrite($clients[$i], $msg);
}else
if($clients[$id]) fwrite($clients[$id], $msg);
}else{
//echo 'senderWorker IS RUNNING...'.chr(10);
usleep(100000);
}
}
}
} class epoll{
private static $socket;
public static $connections;
private static $buffers;
private static $worker;
private static $clients;
function epoll($port, &$worker, &$clients){
self::$clients = $clients;
self::$worker = $worker;
if($port<1024) die("Port must be a number which bigger than 1024\n");
self::$socket = stream_socket_server ('tcp://0.0.0.0:'.$port, $errno, $errstr);
stream_set_blocking(self::$socket, 0);
$base = event_base_new();
$event = event_new();
event_set($event, self::$socket , EV_READ | EV_PERSIST, 'epoll::ev_accept', $base);
event_base_set($event, $base);
event_add($event);
event_base_loop($base); self::$connections = array();
self::$buffers = array();
}
public static function ev_accept($socket, $flag, $base) {
static $id = 0; $connection = stream_socket_accept($socket);
stream_set_blocking($connection, 0); $id += 1; $buffer = event_buffer_new($connection, 'epoll::ev_read', NULL, 'epoll::ev_error', $id);
event_buffer_base_set($buffer, $base);
event_buffer_timeout_set($buffer, 30, 30);
event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff);
event_buffer_priority_set($buffer, 60);//超时自动断开时间
event_buffer_enable($buffer, EV_READ | EV_PERSIST); // we need to save both buffer and connection outside
self::$connections[$id] = $connection;
self::$clients[$id] = $connection;
self::$buffers[$id] = $buffer; echo 'In-> $id='.$id.',$connection='.$connection."\n";
}
public static function ev_error($buffer, $error, $id) {
event_buffer_disable(self::$buffers[$id], EV_READ | EV_WRITE);
event_buffer_free(self::$buffers[$id]);
echo 'Ot-> $id='.$id."\n";
fclose(self::$connections[$id]);
unset(self::$buffers[$id], self::$connections[$id], self::$clients[$id]);
}
public static function ev_read($buffer, $id) {
static $ct=0;
while ($read = event_buffer_read($buffer, 256))
{
$ct+=strlen($read);
if(strpos($read,'ct')!==false) echo 'Ct=>'.count(self::$connections).'\n';
self::$worker[] = array($read, $id);
}
}
//--------------------------------------------
//will not work!!
public static function sendMsg($msg, $id){
if(is_array($id))
foreach($id as $i)
fwrite(self::$clients[$i], $msg);
else if($id===0)
foreach($id as $i)
fwrite(self::$clients[$i], $msg);
else
fwrite(self::$clients[$id], $msg);
}
} //暂未使用,计划用于统计在线之类的
$_statWorker = new statWorker();
$_statWorker->start(); /*
$_logicWorker 处理数据后通过$data传给$_senderWorker下发
因为worker stackable 似乎都不能共享变量(他们接收到的参数——通过worker[]=...,stackable=[],worker->stack几个方式收到的数据都是复制的,并且资源复制不成功,是null,通过public方法设定的参数同样是复制的,只有construct构造函数接收的参数才可以以引用的)
我了解到的是要给worker共享数据,只能通过给其构造函数传递一个stackable(传worker)类型的变量
*/
$data = new data(); //保存连接的客户端,epoll::sendMsg不会正常工作,在里面获取不到self::$clients,也只能通过向构造函数传递stackable变量的方法实现;是为什么我不知道,可能我的用法不对。
$clients = new data(); /*
处理接收上来的数据,比如查看背包,收取邮件,或者战斗 ,行走,但里面不要进行任何io操作——读写数据库甚至是memcache都不可以
logicWorker只做计算型工作,并且是主要的工作线程
那么你可能要问,用户的数据从哪里来?如何保存?
——答案是用别的线程,至少还需要2个线程:登陆线程,从数据库读数据;存档线程,把数据存放到数据。
这样工作按线程分开了,能够有一定并发数, 是多少,不知道,我目前的java程序就是这个方式,单服3000+在线,负载不到1,cpu 20%左右。
php可能要差一点儿 当然这个pthreads应该可以用很不稳定来形容,很容易coredump……,不过怎么都算是个进步吧,因为真的实现了多线程。
*/
$_logicWorker = new logicWorker($data);
/*
负责下发数据,其实,可以由$_logicWorker来执行,我这里主要是想测试数据共享,
*/
$_senderWorker = new senderWorker($data, $clients); //启动线程和监听
$_logicWorker->start();
$_senderWorker->start();
new epoll(9808, $_logicWorker, $clients); echo 'Running ... ---------->
';
我的QQ群:
PHPer&Webgame&移动开发,群号:95303036
加群除了提问之外,请记得帮助别人,谢谢。
一个高在线(可以超过1024)多线程的socket echo server(pthreads 和 libevent扩展)的更多相关文章
- day36——死锁、递归锁、信号量、GIL、多线程实现socket通信、线程池和进程池
day36 死锁现象与递归锁 死锁现象 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这 ...
- 《Unity 3D游戏客户端基础框架》多线程异步 Socket 框架构建
引言: 之前写过一个 demo 案例大致讲解了 Socket 通信的过程,并和自建的服务器完成连接和简单的数据通信,详细的内容可以查看 Unity3D -- Socket通信(C#).但是在实际项目应 ...
- SpringCloud学习系列之一 ----- 搭建一个高可用的注册中心(Eureka)
前言 本篇主要介绍的是SpringCloud相关知识.微服务架构以及搭建一个高可用的服务注册与发现的服务模块(Eureka). SpringCloud介绍 Spring Cloud是在Spring B ...
- 打造一个高逼格的android开源项目——小白全攻略 (转)
转自:打造一个高逼格的android开源项目 小引子 在平时的开发过程中,我们经常会查阅很多的资料,最常参考的是 github 的开源项目.通常在项目的主页面能看到项目的简介和基本使用,并且时不时能看 ...
- PHP如何打造一个高可用高性能的网站呢?
https://blog.csdn.net/jwq101666/article/details/80162245 1. 说到高可用的话要提一下redis,用过的都知道redis是一个具备数据库特征的n ...
- 如何快速开发出一个高质量的APP——创业谈
[起] 今早,一个技术群里有人想快速做出一个app,然后询问技术方案,大概是这样, 拿到了200w投资,期望花20w两个月先做出一个app,包括iOS,Android, 先,呵呵,一下, 大概预估了一 ...
- 【转】一个高端.NET技术人才的2014年度总结
[转]一个高端.NET技术人才的2014年度总结 本人在一家公司做技术负责人.主要从事的是.net方面的开发与管理,偏重开发. 弹指一挥间,时间飘然而过,转眼又是一年. 回顾2014年,是我人生中最 ...
- 【创业积累】如何快速开发出一个高质量的APP
[起] 今早,一个技术群里有人想快速做出一个app,然后询问技术方案,大概是这样, 拿到了200w投资,期望花20w两个月先做出一个app,包括ios,android, 先,呵呵,一下, 大概预估了一 ...
- VopSdk一个高逼格微信公众号开发SDK(源码下载)
看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...
随机推荐
- mysql 内连接 左连接 右连接 外连接
mysql> desc student;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | ...
- java里有没有专门判断List里有重复的数据
public static void main(String[] args) { List<String> list = new ArrayList<Stri ...
- 转:如何学习SQL(第三部分:SQL数据类型与三值逻辑)
转自:http://blog.163.com/mig3719@126/blog/static/285720652010950921286/ 7. 数据类型 在数据库理论中,关系模型和数据类型这两部分内 ...
- 20160805_CentOS6_键盘快捷键
1. 系统 -->首选项 --> 键盘快捷键 2. 3.
- Convention插件 struts零配置
http://blog.csdn.net/spyjava/article/details/13631961系列课程使用 注解:http://www.yiibai.com/struts_2/struts ...
- easyui-datebox 和easyui-datetimebox 设置默认时间当前时间
//显示当前日期 formatterDate = function (date) { var day = date.getDate() > 9 ? date.getDate() : " ...
- Vcenter 添加域管理员权限
授予相应管理权限
- VC++多工程项目
目录 第1章 VC++6.0 1 1.1 设置依赖关系 1 1.2 编译顺序 2 1.3 自动连接 3 1.4 静态库 3 1.4.1 嵌入 3 1.4.2 替换 ...
- 三种语言(c++、as、lua)中函数的差异性
对于不同的语言, 尤其是静态语言和动态语言, 对于函数的定义(即如何看待一个函数)和处理截然不同.具体来说可以分为两类: 1.将函数视为第一类型值, 即函数和其他的对象一样, 都是语言中一个普通的对象 ...
- 我常用的Webstorm快捷键
ctrl+b 跳转到方法内部 meta:vp + tab 生成移动端 ctrl + alt + l 整理代码 ctrl + shift + up/down 行移动 ctrl + shift + ent ...