为了账号安全,请及时绑定邮箱和手机立即绑定

php stream_socket_pair 乱入如何解决??

php stream_socket_pair 乱入如何解决??

PHP
慕村225694 2019-03-18 15:31:15
本来使用 stream_socket_pair 是用来父子进程间通信的,实现多进程下,某个子进程消息通过父进程转发给其他子进程(类似于 qq),但是这碰到了一个问题: $count = 4; $childConn = []; $parent = null; for ($i = 0; $i < $count; ++$i) { $pair = stream_socket_pair(STREAM_PF_UNIX , STREAM_SOCK_STREAM , STREAM_IPRROTO_IP); $pid = pcntl_fork(); if ($pid < 0) { throw new Exception("创建子进程失败"); } else if ($pid > 0) { fclose($pair[1]); $child = $pair[0]; $childConn[$pid] = $child; // 监听子进程消息 listenChild($child); } else { fclose($pair[0]); $parent = $pair[1]; // 监听父进程消息 listenParent($parent); } } 以上目的就是为每个子进程创建与父进程的通信通道,然结果却发生了令人意外的效果! 比如下面一个通信过程: 父进程pid: 30246 子进程pid: 30247 30248 30249 30250,每个子进程中都监听客户端连接。 1. 客户端A 进入子进程 30247 2. 客户端B 进入子进程 30248 3. A 客户端发送消息,子进程 30247 接受到消息,要求转发给 30248 中的客户端 B 4. 子进程 30247 调用事先保存的 $parent(与父进程的通信连接),发送消息 ... 意外产生了!! 你可能想当然的以为,他绝对是发送给父进程(30246)! 而实际上不是!结果可能是其他子进程! 到这儿,我就很郁闷了,stream_socket_pair 创建的成对套接字,怎么会发生这种现象?? 我表示特别无语了 ....跪求大神解救
查看完整描述

1 回答

?
慕森卡

TA贡献1806条经验 获得超8个赞

坑人代码还原如下

use Event\Event;
use Event\Select;

$pid_list = [];
$parent_pid = posix_getpid();

for ($i = 0; $i < 4; ++$i)
{
    $pair = stream_socket_pair(STREAM_PF_UNIX , STREAM_SOCK_STREAM , STREAM_IPPROTO_IP);
    
    $pid = pcntl_fork();
    
    if ($pid < 0) {
        throw new Exception("创建子进程失败");
    } else if ($pid > 0) {
        // 父进程
        fclose($pair[0]);
        
        $child = $pair[1];
        
        fwrite($child , "父进程问候 " . posix_getpid());
        
        Select::addIo($child , Event::READ , function($ctrl , $socket , $child) use($parent_pid){
            $msg = fread($socket , 65535);
            
            echo "父进程领域!父进程 {$parent_pid} 当前执行进程 " . posix_getpid() . " 消息:{$msg}" . PHP_EOL;
        } , $child);
    } else {
        // 子进程
        fclose($pair[1]);
        $parent = $pair[0];
        fwrite($parent , "子进程问候 " . posix_getpid());
        
        Select::addIo($parent , Event::READ , function($ctrl , $socket , $parent) use($parent_pid){
            $msg = fread($socket , 65535);
            
            echo "子进程领域!子进程 " . posix_getpid() . " 当前执行进程 " . posix_getpid() . " 消息:{$msg}\n";
        } , $parent);
    }
}

产生的进程信息:

父进程:32140
子进程:32141 32142 32143 32144

坑人的结果:

父进程代码领域下(子进程乱入到父进程领域!)

父进程领域!父进程 32140 当前执行进程 32142 消息 子进程问候 32141
父进程领域!父进程 32140 当前执行进程 32143 消息 子进程问候 32142
父进程领域!父进程 32140 当前执行进程 32144 消息 子进程问候 32143
父进程领域!父进程 32140 当前执行进程 32140 消息 子进程问候 32144

子进程代码领域(正确)

子进程领域!子进程 32141 当前执行进程 32141 消息 父进程问候 32140
子进程领域!子进程 32142 当前执行进程 32142 消息 父进程问候 32140
子进程领域!子进程 32143 当前执行进程 32143 消息 父进程问候 32140
子进程领域!子进程 32144 当前执行进程 32144 消息 父进程问候 32140

分析:为什么子进程会调用在父进程定义的事件呢??

这得仔细分析 for 循环!

  • 第一次循环,父进程向 Event::$events 添加子进程监听事件,子进程平行执行。对产生的第一个子进程来说, Event::$events 为空,所以没有拷贝到父进程的事件。
  • 第二次循环,父进程向 Event::$events 再次添加监听子进程事件,此时,对产生的第二个子进程来说,Event::$events 存在一个事件!从父进程拷贝了一个事件。
  • 第三次循环,父进程再次向 Event::$events 再次添加监听子进程事件,此时,对产生的三个子进程来说, Event::$events 存在两个事件!从父进程拷贝了两个事件。
  • 第四次循环,父进程再次向 Event::$evnets 添加事件,此时,相对产生的第四个子进程来说,Event::$events 存在三个事件,从父进程拷贝了三个事件。

由上可知,子进程实际也在监听从父进程拷贝的事件,如果事件触发,那么监听该事件的父子进程实际上都会触发。从而出现了令人郁闷的:子进程乱入到了父进程的领域。

解决方法

在子进程中,把从父进程拷贝的事件销毁即可。

for ($i = 0; $i < 4; ++$i)
{
    $pid = pcntl_fork();
    
    if ($pid < 0) {
        throw new Exception("创建子进程失败");
    } else if ($pid > 0) {
        // 父进程
        Select::addIo( .... );
        // ...添加相关事件
    } else {
        // 子进程
        // 销毁从父进程拷贝的事件
        Select::clear();
        
        // ....做些什么
    }
}
查看完整回答
反对 回复 2019-03-18
  • 1 回答
  • 0 关注
  • 346 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信