1. tcp 服务端简单demo与client .
 <?php
/**
* author : rookiejin <mrjnamei@gmail.com>
* createTime : 2018/1/4 10:26
* description: tcp.php - swoole-demo
* 该代码是一份简单的面向对象形式的 tcp 服务器和客户端通讯的demo
* 功能:实现服务器端tcp简单demo
*/
// 创建一个tcp服务器 $server = new swoole_server("127.0.0.1", 9501); /**
* @var $server swoole_server
* @var $fd int 文件描述符
*/
$server->on("connect", function($server , $fd){
echo "a client connected\n" ;
}); /**
* @var $server swoole_server
* @var $fd int 文件描述符
* @var $from_id worker_id worker进程id
* @var $data 接受的数据
*/
$server->on("receive", function($server , $fd , $from_id ,$data){
echo "#server received msg:" , $data , "\n";
$server->send($fd , "i received");
}); /**
* @var $server swoole_server
* @var $fd 文件描述符
*/
$server->on("close",function($server, $fd){
echo "# client closed\n";
});
// 启动服务器
$server->start();
client.php <?php $client = new swoole_client(SWOOLE_SOCK_TCP); if(!$client->connect("127.0.0.1", 9501, -1)){
exit("connect failed" . $client->errCode . "\n");
} $client->send("helloworld");
echo $client->recv() , "\n";
$client->close();

2. 使用面向对象的方式来写TCP服务器.

 <?php
/**
* author : rookiejin <mrjnamei@gmail.com>
* createTime : 2018/1/4 10:26
* description: php_oop.php - swoole-demo
* 该代码是一份干净的tcp server 事件回调,
* 没有任何对事件回调的业务处理 .
* 以该代码为基准,后面的demo都在此基础上修改 .
*/ class Server { /**
* @var \swoole_server
*/
public $server ; /**
* 配置项
* @var $config array
*/
public $config ; /**
* @var \Server
*/
public static $_worker ; /**
* 存储pid文件的位置
*/
public $pidFile ; /**
* worker 进程的数量
* @var $worker_num
*/
public $worker_num; /**
* 当前进程的worker_id
* @var $worker_id
*/
public $worker_id ; /**
* task 进程数 + worker 进程数 = 总的服务进程
* 给其他的进程发送消息:
* for($i = 0 ; $i < $count ; $i ++) {
* if($i == $this->worker_id) continue;表示是该进程
* $this->server->sendMessage($i , $data);
* }
* task 进程的数量
* @var $task_num
*/
public $task_num ; /**
* Server constructor.
*
* @param array $config
*/
public function __construct(array $config)
{
$this->server = new swoole_server($config ['host'] , $config ['port']);
$this->config = $config;
$this->serverConfig();
self::$_worker = & $this; // 引用
} public function serverConfig()
{
$this->server->set($this->config['server']);
} public function start()
{
// Server启动在主进程的主线程回调此函数
$this->server->on("start",[$this , "onSwooleStart"]);
// 此事件在Server正常结束时发生
$this->server->on("shutDown", [$this , "onSwooleShutDown"]);
//事件在Worker进程/Task进程启动时发生。这里创建的对象可以在进程生命周期内使用。
$this->server->on("workerStart", [$this , "onSwooleWorkerStart"]);
// 此事件在worker进程终止时发生。在此函数中可以回收worker进程申请的各类资源。
$this->server->on("workerStop",[$this, "onSwooleWorkerStop"]);
// worker 向task_worker进程投递任务触发
$this->server->on("task", [$this, "onSwooleTask"]);
// task_worker 返回值传给worker进程时触发
$this->server->on("finish",[$this , "onSwooleFinish"]);
// 当工作进程收到由 sendMessage 发送的管道消息时会触发onPipeMessage事件
$this->server->on("pipeMessage",[$this ,"onSwoolePipeMessage"]);
// 当worker/task_worker进程发生异常后会在Manager进程内回调此函数
$this->server->on("workerError", [$this , "onSwooleWrokerError"]);
// 当管理进程启动时调用它,函数原型:
$this->server->on("managerStart", [$this , "onSwooleManagerStart"]);
// onManagerStop
$this->server->on("managerStop", [$this , "onSwooleManagerStop"]);
// 有新的连接进入时,在worker进程中回调。
$this->server->on("connect" , [$this ,'onSwooleConnect']);
// 接收到数据时回调此函数,发生在worker进程中
$this->server->on("receive", [$this, 'onSwooleReceive']);
//CP客户端连接关闭后,在worker进程中回调此函数。函数原型:
$this->server->on("close", [$this ,"onSwooleClose"]);
$this->server->start();
} /**
* @warning 进程隔离
* 该步骤一般用于存储进程的 master_pid 和 manager_pid 到文件中
* 本例子存储的位置是 __DIR__ . "/tmp/" 下面
* 可以用 kill -15 master_pid 发送信号给进程关闭服务器,并且触发下面的onSwooleShutDown事件
* @param $server
*/
public function onSwooleStart($server)
{
$this->setProcessName('SwooleMaster');
$debug = debug_backtrace();
$this->pidFile = __DIR__ . "/temp/" . str_replace("/" , "_" , $debug[count($debug) - 1] ["file"] . ".pid" );
$pid = [$server->master_pid , $server->manager_pid];
file_put_contents($this->pidFile , implode(",", $pid));
} /**
* @param $server
* 已关闭所有Reactor线程、HeartbeatCheck线程、UdpRecv线程
* 已关闭所有Worker进程、Task进程、User进程
* 已close所有TCP/UDP/UnixSocket监听端口
* 已关闭主Reactor
* @warning
* 强制kill进程不会回调onShutdown,如kill -9
* 需要使用kill -15来发送SIGTREM信号到主进程才能按照正常的流程终止
* 在命令行中使用Ctrl+C中断程序会立即停止,底层不会回调onShutdown
*/
public function onSwooleShutDown($server)
{
echo "shutdown\n";
} /**
* @warning 进程隔离
* 该函数具有进程隔离性 ,
* {$this} 对象从 swoole_server->start() 开始前设置的属性全部继承
* {$this} 对象在 onSwooleStart,onSwooleManagerStart中设置的对象属于不同的进程中.
* 因此这里的pidFile虽然在onSwooleStart中设置了,但是是不同的进程,所以找不到该值.
* @param \swoole_server $server
* @param int $worker_id
*/
public function onSwooleWorkerStart(swoole_server $server, int $worker_id)
{
if($this->isTaskProcess($server))
{
$this->setProcessName('SwooleTask');
}
else{
$this->setProcessName('SwooleWorker');
}
$debug = debug_backtrace();
$this->pidFile = __DIR__ . "/temp/" . str_replace("/" , "_" , $debug[count($debug) - 1] ["file"] . ".pid" );
file_put_contents($this->pidFile , ",{$worker_id}" , FILE_APPEND);
} public function onSwooleWorkerStop($server,$worker_id)
{
echo "#worker exited {$worker_id}\n";
} /**
* @warning 进程隔离 在task_worker进程内被调用
* worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务
* $task_id和$src_worker_id组合起来才是全局唯一的,不同的worker进程投递的任务ID可能会有相同
* 函数执行时遇到致命错误退出,或者被外部进程强制kill,当前的任务会被丢弃,但不会影响其他正在排队的Task
* @param $server
* @param $task_id 是任务ID 由swoole扩展内自动生成,用于区分不同的任务
* @param $src_worker_id 来自于哪个worker进程
* @param $data 是任务的内容
* @return mixed $data
*/
public function onSwooleTask($server , $task_id, $src_worker_id,$data)
{
return $data ;
} public function onSwooleFinish()
{ } /**
* 当工作进程收到由 sendMessage 发送的管道消息时会触发onPipeMessage事件。worker/task进程都可能会触发onPipeMessage事件。
* @param $server
* @param $src_worker_id 消息来自哪个Worker进程
* @param $message 消息内容,可以是任意PHP类型
*/
public function onSwoolePipeMessage($server , $src_worker_id,$message)
{ } /**
* worker进程发送错误的错误处理回调 .
* 记录日志等操作
* 此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程CoreDump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。
* @param $server
* @param $worker_id 是异常进程的编号
* @param $worker_pid 是异常进程的ID
* @param $exit_code 退出的状态码,范围是 1 ~255
* @param $signal 进程退出的信号
*/
public function onSwooleWrokerError($server ,$worker_id,$worker_pid,$exit_code,$signal)
{
echo "#workerError:{$worker_id}\n";
} /**
*
*/
public function onSwooleManagerStart()
{
$this->setProcessName('SwooleManager');
} /**
* @param $server
*/
public function onSwooleManagerStop($server)
{
echo "#managerstop\n";
} /**
* 客户端连接
* onConnect/onClose这2个回调发生在worker进程内,而不是主进程。
* UDP协议下只有onReceive事件,没有onConnect/onClose事件
* @param $server
* @param $fd
* @param $reactorId
*/
public function onSwooleConnect($server ,$fd ,$reactorId)
{
echo "#connected\n";
} /**
* @param $server server对象
* @param $fd 文件描述符
* @param $reactorId reactor线程id
*/
public function onSwooleReceive($server,$fd,$reactorId)
{
echo "#received\n";
} /**
* 连接断开,广播业务需要从redis | memcached | 内存 中删除该fd
* @param $server
* @param $fd
* @param $reactorId
*/
public function onSwooleClose($server, $fd ,$reactorId)
{
echo "#swooleClosed\n" ;
} public function setProcessName($name)
{
if(function_exists('cli_set_process_title'))
{
@cli_set_process_title($name);
}
else{
@swoole_set_process_name($name);
}
} /**
* 返回真说明该进程是task进程
* @param $server
* @return bool
*/
public function isTaskProcess($server)
{
return $server->taskworker === true ;
} /**
* main 运行入口方法
*/
public static function main()
{
self::$_worker->start();
}
} $config = ['server' => ['worker_num' => 4 , "task_worker_num" => "20" , "dispatch_mode" => 3 ] , 'host' => '0.0.0.0' , 'port' => 9501];
$server = new Server($config);
Server::main() ;
  1. 本例子是注册了swoole的基本事件监听,回调没有做,为下面的代码先做一份铺垫。
  2. 这里主要要讲的是swoole的启动步骤.
* note
1. $server = new Server ($config);
在new了一个单例Server类以后,将Server::$_worker代理到本身$this. 并且做好服务器的配置.
2. Server::main();
该代码为server注册事件回调函数. 然后启动 swoole_server::start();

3. 启动过程:
1). 先会开启master进程. 触发onSwooleStart事件,
可以获取到master进程的pid和manager进程的pid.
该函数式在master进程执行的.在事件回调里实例化的任何对象只针对master进程有效.
2). 接着触发onSwooleManagerStart. 在manager进程中执行.
该函数式在master进程执行的.在事件回调里实例化的任何对象只针对manager进程有效.
3). 接着触发onSwooleWorkerStart. 该过程启动worker与task_worker进程.步骤是并行的,
不分先后.
worker进程与task_worker进程其实一样,都属于worker进程,具有进程隔离性,自己进程内
实例化的类只有在自己进程内部有用,
worker_num + worker_task_num = 总的worker_id数量.
如果需要从worker进程向其他进程发送消息的话,可以这么做:
    for($i = 0 ; $i < $worker_num + $task_worker_num ; $i ++) {
if($i == $this->worker_id) continue;
$this->server_sendMessage($i ,$message);
}

4). 然后监听connect与receive事件, 就属于具体的业务范畴了.

下面带来一个 tcp 聊天室的简单案例,例子是用swoole_table存储所有的链接信息. 功能可以实现群聊,单聊: 主要业务逻辑在onSwooleReceive回调中

 <?php
/**
* author : rookiejin <mrjnamei@gmail.com>
* createTime : 2018/1/4 10:26
* description: tcp_get_and_send.php - swoole-demo
* 该代码是一份简单的面向对象形式的 tcp 服务器和客户端通讯的demo
* 功能:单发. 群发.
*/ class Server { /**
* @var \swoole_server
*/
public $server ; /**
* 配置项
* @var $config array
*/
public $config ; /**
* @var \Server
*/
public static $_worker ; /**
* 存储pid文件的位置
*/
public $pidFile ; /**
* worker 进程的数量
* @var $worker_num
*/
public $worker_num; /**
* 当前进程的worker_id
* @var $worker_id
*/
public $worker_id ; /**
* task 进程数 + worker 进程数 = 总的服务进程
* 给其他的进程发送消息:
* for($i = 0 ; $i < $count ; $i ++) {
* if($i == $this->worker_id) continue;表示是该进程
* $this->server->sendMessage($i , $data);
* }
* task 进程的数量
* @var $task_num
*/
public $task_num ; /**
* @var $table swoole_table 内存表
*/
public $table; /**
* Server constructor.
*
* @param array $config
*/
public function __construct(array $config)
{
$this->server = new swoole_server($config ['host'] , $config ['port']);
$this->config = $config;
$this->serverConfig();
$this->createTable();
self::$_worker = & $this; // 引用
} private function serverConfig()
{
$this->server->set($this->config['server']);
} /**
* 创建swoole_table
*/
private function createTable()
{
$this->table = new swoole_table( 65536 );
$this->table->column("fd",swoole_table::TYPE_INT , 8);
$this->table->column("worker_id", swoole_table::TYPE_INT , 4);
$this->table->column("name",swoole_table::TYPE_STRING,255);
$this->table->create();
} public function start()
{
// Server启动在主进程的主线程回调此函数
$this->server->on("start",[$this , "onSwooleStart"]);
// 此事件在Server正常结束时发生
$this->server->on("shutDown", [$this , "onSwooleShutDown"]);
//事件在Worker进程/Task进程启动时发生。这里创建的对象可以在进程生命周期内使用。
$this->server->on("workerStart", [$this , "onSwooleWorkerStart"]);
// 此事件在worker进程终止时发生。在此函数中可以回收worker进程申请的各类资源。
$this->server->on("workerStop",[$this, "onSwooleWorkerStop"]);
// worker 向task_worker进程投递任务触发
$this->server->on("task", [$this, "onSwooleTask"]);
// task_worker 返回值传给worker进程时触发
$this->server->on("finish",[$this , "onSwooleFinish"]);
// 当工作进程收到由 sendMessage 发送的管道消息时会触发onPipeMessage事件
$this->server->on("pipeMessage",[$this ,"onSwoolePipeMessage"]);
// 当worker/task_worker进程发生异常后会在Manager进程内回调此函数
$this->server->on("workerError", [$this , "onSwooleWrokerError"]);
// 当管理进程启动时调用它,函数原型:
$this->server->on("managerStart", [$this , "onSwooleManagerStart"]);
// onManagerStop
$this->server->on("managerStop", [$this , "onSwooleManagerStop"]);
// 有新的连接进入时,在worker进程中回调。
$this->server->on("connect" , [$this ,'onSwooleConnect']);
// 接收到数据时回调此函数,发生在worker进程中
$this->server->on("receive", [$this, 'onSwooleReceive']);
//CP客户端连接关闭后,在worker进程中回调此函数。函数原型:
$this->server->on("close", [$this ,"onSwooleClose"]);
$this->server->start();
} /**
* @warning 进程隔离
* 该步骤一般用于存储进程的 master_pid 和 manager_pid 到文件中
* 本例子存储的位置是 __DIR__ . "/tmp/" 下面
* 可以用 kill -15 master_pid 发送信号给进程关闭服务器,并且触发下面的onSwooleShutDown事件
* @param $server
*/
public function onSwooleStart($server)
{
$this->setProcessName('SwooleMaster');
$debug = debug_backtrace();
$this->pidFile = __DIR__ . "/temp/" . str_replace("/" , "_" , $debug[count($debug) - 1] ["file"] . ".pid" );
$pid = [$server->master_pid , $server->manager_pid];
file_put_contents($this->pidFile , implode(",", $pid));
} /**
* @param $server
* 已关闭所有Reactor线程、HeartbeatCheck线程、UdpRecv线程
* 已关闭所有Worker进程、Task进程、User进程
* 已close所有TCP/UDP/UnixSocket监听端口
* 已关闭主Reactor
* @warning
* 强制kill进程不会回调onShutdown,如kill -9
* 需要使用kill -15来发送SIGTREM信号到主进程才能按照正常的流程终止
* 在命令行中使用Ctrl+C中断程序会立即停止,底层不会回调onShutdown
*/
public function onSwooleShutDown($server)
{
echo "shutdown\n";
} /**
* @warning 进程隔离
* 该函数具有进程隔离性 ,
* {$this} 对象从 swoole_server->start() 开始前设置的属性全部继承
* {$this} 对象在 onSwooleStart,onSwooleManagerStart中设置的对象属于不同的进程中.
* 因此这里的pidFile虽然在onSwooleStart中设置了,但是是不同的进程,所以找不到该值.
* @param \swoole_server $server
* @param int $worker_id
*/
public function onSwooleWorkerStart(swoole_server $server, int $worker_id)
{
if($this->isTaskProcess($server))
{
$this->setProcessName('SwooleTask');
}
else{
$this->setProcessName('SwooleWorker');
}
$debug = debug_backtrace();
$this->pidFile = __DIR__ . "/temp/" . str_replace("/" , "_" , $debug[count($debug) - 1] ["file"] . ".pid" );
file_put_contents($this->pidFile , ",{$worker_id}" , FILE_APPEND);
} public function onSwooleWorkerStop($server,$worker_id)
{
echo "#worker exited {$worker_id}\n";
} /**
* @warning 进程隔离 在task_worker进程内被调用
* worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务
* $task_id和$src_worker_id组合起来才是全局唯一的,不同的worker进程投递的任务ID可能会有相同
* 函数执行时遇到致命错误退出,或者被外部进程强制kill,当前的任务会被丢弃,但不会影响其他正在排队的Task
* @param $server
* @param $task_id 是任务ID 由swoole扩展内自动生成,用于区分不同的任务
* @param $src_worker_id 来自于哪个worker进程
* @param $data 是任务的内容
* @return mixed $data
*/
public function onSwooleTask($server , $task_id, $src_worker_id,$data)
{
// todo
} public function onSwooleFinish()
{
// todo
} /**
* 当工作进程收到由 sendMessage 发送的管道消息时会触发onPipeMessage事件。worker/task进程都可能会触发onPipeMessage事件。
* @param $server
* @param $src_worker_id 消息来自哪个Worker进程
* @param $message 消息内容,可以是任意PHP类型
*/
public function onSwoolePipeMessage($server , $src_worker_id,$message)
{
// todo
} /**
* worker进程发送错误的错误处理回调 .
* 记录日志等操作
* 此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程CoreDump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。
* @param $server
* @param $worker_id 是异常进程的编号
* @param $worker_pid 是异常进程的ID
* @param $exit_code 退出的状态码,范围是 1 ~255
* @param $signal 进程退出的信号
*/
public function onSwooleWrokerError($server ,$worker_id,$worker_pid,$exit_code,$signal)
{
echo "#workerError:{$worker_id}\n";
} /**
*
*/
public function onSwooleManagerStart()
{
$this->setProcessName('SwooleManager');
} /**
* @param $server
*/
public function onSwooleManagerStop($server)
{
echo "#managerstop\n";
} /**
* 客户端连接
* onConnect/onClose这2个回调发生在worker进程内,而不是主进程。
* UDP协议下只有onReceive事件,没有onConnect/onClose事件
* @param $server
* @param $fd
* @param $reactorId
*/
public function onSwooleConnect($server ,$fd ,$reactorId)
{
echo "#{$fd} has connected\n";
$server->send($fd , "please input your name\n");
} /**
* @param $server server对象
* @param $fd 文件描述符
* @param $reactorId reactor线程id
* @param $data 数据
*/
public function onSwooleReceive($server,$fd,$reactorId, $data)
{
$data = json_decode($data, true);
$exist = $this->table->exist($fd);
$from = $data ['from'];
$to = $data ['to'];
$message = $data ['message']; if(!$exist) {
foreach ($this->table as $row)
{
if($row ['name'] == $from)
{
$server->send($fd , 'name already exists');
return ;
}
}
$this->table->set($fd , ['name' => $from , 'fd' => $fd]);
$server->send($fd , "welcome to join tcp chat room\n");
}
// 发送给其他人 .
if($to == 'all')
{
$this->sendToAllExceptHim($server , $message , $fd);
return ;
}
if(!empty($to) && !empty($message))
{
$this->sendToOne($server ,$message ,$to);
}
return ;
} private function sendToOne($server , $message , $name)
{
foreach ($this->table as $row)
{
if($row ['name'] == $name)
{
$server->send($row ['fd'] , $message);
return ;
}
}
} private function sendToAllExceptHim($server , $message, $fd)
{
foreach ($this->table as $row)
{
if($row['fd'] == $fd) continue ;
$server->send($row ['fd'] , $message);
}
} /**
* 连接断开,广播业务需要从redis | memcached | 内存 中删除该fd
* @param $server
* @param $fd
* @param $reactorId
*/
public function onSwooleClose($server, $fd ,$reactorId)
{
$this->table->del($fd);
} public function setProcessName($name)
{
if(function_exists('cli_set_process_title'))
{
@cli_set_process_title($name);
}
else{
@swoole_set_process_name($name);
}
} /**
* 返回真说明该进程是task进程
* @param $server
* @return bool
*/
public function isTaskProcess($server)
{
return $server->taskworker === true ;
} /**
* main 运行入口方法
*/
public static function main()
{
self::$_worker->start();
}
} $config = ['server' => ['worker_num' => 4 , "task_worker_num" => "20" , "dispatch_mode" => 3 ] , 'host' => '0.0.0.0' , 'port' => 9501];
$server = new Server($config);
Server::main() ;

如有错误,敬请纠正!

最新文章

  1. iOS代码规范
  2. socket是什么?(翻译)
  3. spark发行版笔记10
  4. 使用 CSS 去掉 iPhone 网页上按钮的超大圆角以及文本框圆角默认样式
  5. 把你的旧笔记本变成 Chromebook
  6. java 词法分析器
  7. php 简单的验证码
  8. 自定义控件学习之菜鸟笔记一(Hello World)
  9. JavaScript基础(语法类型转换、运算符、语句)
  10. Mybatis 示例之 复杂(complex)属性(property)
  11. js onblur 和 onkeyup 事件用法
  12. ZOJ 3699 Dakar Rally(贪心)
  13. 201521123002《Java程序设计》第9周学习总结
  14. R语言︱文本挖掘——jiabaR包与分词向量化的simhash算法(与word2vec简单比较)
  15. Sublime text使用快捷键
  16. 2018-2019-2 《网络对抗技术》Exp0 Kali安装 Week1 20165319
  17. markdown笔记实现页内目录跳转
  18. Egret 4.x 和 5.x 项目共存的方法
  19. HTTPS 如何保证数据传输的安全性
  20. 170811、Java获取jdk系统环境变量

热门文章

  1. Java 面试-即时编译( JIT )
  2. Oracle联合注入总结
  3. Android Studio 模拟器Intel 加速
  4. go-异常处理-error-panic-recover
  5. 关于JavaScript if...else &amp; if 判断简写
  6. 03 Node.js学习笔记之根据http请求路径返回不同数据
  7. ios 11 系统CPU过高,xib中textfield使用导致出过高
  8. 字符编码 + python2和python3的编码区别(day08整理)
  9. Java基础(42)AbstractSet类
  10. JVM内存结构、参数调优和内存泄露分析