研究了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扩展)的更多相关文章

  1. day36——死锁、递归锁、信号量、GIL、多线程实现socket通信、线程池和进程池

    day36 死锁现象与递归锁 死锁现象 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这 ...

  2. 《Unity 3D游戏客户端基础框架》多线程异步 Socket 框架构建

    引言: 之前写过一个 demo 案例大致讲解了 Socket 通信的过程,并和自建的服务器完成连接和简单的数据通信,详细的内容可以查看 Unity3D -- Socket通信(C#).但是在实际项目应 ...

  3. SpringCloud学习系列之一 ----- 搭建一个高可用的注册中心(Eureka)

    前言 本篇主要介绍的是SpringCloud相关知识.微服务架构以及搭建一个高可用的服务注册与发现的服务模块(Eureka). SpringCloud介绍 Spring Cloud是在Spring B ...

  4. 打造一个高逼格的android开源项目——小白全攻略 (转)

    转自:打造一个高逼格的android开源项目 小引子 在平时的开发过程中,我们经常会查阅很多的资料,最常参考的是 github 的开源项目.通常在项目的主页面能看到项目的简介和基本使用,并且时不时能看 ...

  5. PHP如何打造一个高可用高性能的网站呢?

    https://blog.csdn.net/jwq101666/article/details/80162245 1. 说到高可用的话要提一下redis,用过的都知道redis是一个具备数据库特征的n ...

  6. 如何快速开发出一个高质量的APP——创业谈

    [起] 今早,一个技术群里有人想快速做出一个app,然后询问技术方案,大概是这样, 拿到了200w投资,期望花20w两个月先做出一个app,包括iOS,Android, 先,呵呵,一下, 大概预估了一 ...

  7. 【转】一个高端.NET技术人才的2014年度总结

    [转]一个高端.NET技术人才的2014年度总结  本人在一家公司做技术负责人.主要从事的是.net方面的开发与管理,偏重开发. 弹指一挥间,时间飘然而过,转眼又是一年. 回顾2014年,是我人生中最 ...

  8. 【创业积累】如何快速开发出一个高质量的APP

    [起] 今早,一个技术群里有人想快速做出一个app,然后询问技术方案,大概是这样, 拿到了200w投资,期望花20w两个月先做出一个app,包括ios,android, 先,呵呵,一下, 大概预估了一 ...

  9. VopSdk一个高逼格微信公众号开发SDK(源码下载)

    看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...

随机推荐

  1. 06 SQL执行计划

    解释计划 与 执行计划的 区别 随着可以得到解释计划输出的开发工具, 比如 toad 的普遍使用, 生成解释计划就变的相当简单. 而不简单的是得到执行计划. 解释计划 EXPLAIN PLAN 用来显 ...

  2. nodejs学习笔记<二>简单的node服务器

    在环境搭建好后,就可以开始动手架设(node驱动)一个简单的web服务器. 首先,nodejs还是用js编写.先来看一段node官网上的实例代码. var http = require('http') ...

  3. 【转载】【Centos linux系统】命令行(静默)安装oracle 11gR2

    [原文]:http://blog.chinaunix.net/uid-23886490-id-3565998.html 一.安装前准备 1.内存及swap要求 至于swap如何添加,后文将提到 gre ...

  4. J2EE 第二阶段项目之编写代码(四)

    我的任务就是项目统计. 1 效益统计 1 教育效益统计表 (教育效益统计表,增,改,查看,查) 2 农牧林效益统计表 (农牧林效益统计表,增,改,查看,查) 3 乡村效益统计表    (乡村效益统计表 ...

  5. 转!sqlServer2000 表连接查询

    在查询多个表时,我们经常会用“连接查询”.连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的一个标志. 什么是连接查询呢? 概念:根据两个表或多个表的列之间的关系,从这些表中查询数据 ...

  6. 20160805_CentOS6_控制台切换

    1. Ctrl + Alt + F1~F6 Ctrl + Alt + F1 是 图形界面(如果装了的话),后面的是 控制台界面 2. 3.

  7. python中列表的操作

    list1 = ['A' , 'B' , 'C'] list1[0] ; list1[-1] # 取第一个和最后一个元素 list1[ : ] ; list1[ : len(list1)] # 取所有 ...

  8. javaNIO学习

    Buffer其实就是是一个容器对象,它包含一些要写入或者刚读出的数据.在NIO中加入Buffer对象,体现了新库与原I/O的一个重要区别.在面向流的I/O中,您将数据直接写入或者将数据直接读到Stre ...

  9. 网络编程socket基本API详解(转)

    网络编程socket基本API详解   socket socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket ...

  10. URL链接中文参数乱码的若干处理方法

    JAVA 中URL链接中文参数乱码的若干处理方法,现在整理收录如下: 方法一: (1) JS中,在URL参数中确保用UTF-8编码,用js函数encodeURI()编码,例如 url:"xx ...