php 进程通信系列 (五)socket unix域套接字

PHP Unix域套接字通信
本文介绍PHP中Unix域套接字的原理与实践,包括无名与命名套接字的区别,通过示例演示了如何利用Unix域套接字进行TCP及UDP类型的进程间通信。

常见进程通信方式

在这里插入图片描述

Unix socket (套接字)介绍

现实世界中两个人进行信息交流的整个过程被称作一次通信(Communication),通信的双方被称为端点(Endpoint)。

工具通讯环境的不同,端点之间可以选择不同的工具进行通信,距离近可以直接对话,距离远可以选择打电话、微信聊天。这些工具就被称为 Socket。
在这里插入图片描述

同理,在计算机中也有类似的概念:
在 Unix 中,一次通信由两个端点组成,例如 HTTP 服务端和 HTTP 客户端。
端点之间想要通信,必须借助某些工具,Unix 中端点之间使用 Socket 来进行通信。

Socket 原本是为网络通信而设计的,但后来在 Socket 的框架上发展出一种 IPC 机制,就是 UDS。
Unix Domain Socket(UDS,Unix 域套接字),它还有另一个名字叫 IPC(inter-process communication,进程间通信)。

使用 UDS 的好处显而易见:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC 机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的

UDS 与网络 Socket 最明显的区别在于,网络 Socket 地址是 IP 地址加端口号,而 UDS 的地址是一个 Socket 类型的文件在文件系统中的路径,一般名字以 .sock 结尾。

这个 Socket 文件可以被系统进程引用,两个进程可以同时打开一个 UDS 进行通信,而且这种通信方式只会发生在系统内核里,不会在网络上进行传播。

特别说明:本文中提到的“Socket”、“网络套接字”、“套接字”,如无特殊指明,指的都是同一个东西。

实际上,Socket 通信不仅可以跨网络与不同主机的进程间通信,还可以在同主机上进程间通信,这就是今天介绍的 unix 域套接字。

一些理论基础:

  1. 套接字是什么?

套接字也就是socket实际上是一种连接

  1. 套接字通信域类型:

ipv4[AF_INET] 是跨机器,通信需要经过网卡,需要绑定IP,端口(绑定的原因是便于寻址,ip主要用于确认通信的机器,端口用于确定与哪个进程通信)

ipv6[AF_INET6] 是跨机器,通信需要经过网卡,需要绑定IP,端口

UNIX[AF_UNIX,AF_LOCAL] unix域也叫本地域,通信的时候不需要绑定ip端口不需要网卡,不经过网络

unix 域套接字有两种与管道类似,分为有命名的【创建好的unix域套接字需要绑定地址,它是一个比较特殊文件,socket 文件】,既能实现父子进程通信,也能实现不同进程间通信

无命名的【创建好的unix域套接字不需要绑定地址】,只能实现血缘关系进程通信,比如父子进程通信

  1. 比较常用套接字类型:tcp,udp

tcp: 需要连接(三次握手),是可靠的,有序的,错误丢包可重传的,字节流服务

udp: 不需要连接,不可靠,数据长度固定的,数据报服务

注意: 本地 socket 的编程接口和 IPv4 、IPv6 套接字编程接口是一致的,可以支持「字节流」和「数据报」两种协议,也就是说unix 域套接字也支持 tpc,upd 两种套接字类型,不过对于unix 域的 upd是可靠的,有序的这个由内核实现

我们来看看php创建 无命名本地域 socket 的函数:

//创建一对无命名的,相互连接的UNIX 域套接字
stream_socket_pair(int $domain, int $type, int $protocol): array|false

三个参数分别代表:

  • domain 参数用来指定协议族,比如 STREAM_PF_INET 用于 IPV4、STREAM_PF_INET6 用于 IPV6、STREAM_PF_UNIX 用于本机;
  • type 参数用来指定通信特性,比如 STREAM_SOCK_STREAM 表示的是字节流,对应 TCP、STREAM_SOCK_DGRAM 表示的是数据报,对应 UDP、STREAM_SOCK_RAW 表示的是原始套接字;
  • protocal 参数原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成,protocol 目前一般写成 0 即可;

根据创建 socket 类型的不同,通信的方式也就不同:

  • 实现 TCP 字节流通信: socket 类型是 STREAM_PF_INET 和 STREAM_SOCK_STREAM;
  • 实现 UDP 数据报通信:socket 类型是 STREAM_PF_INET 和 STREAM_SOCK_DGRAM;
  • 实现本地进程间通信:
    「本地字节流 socket 」类型是 STREAM_PF_UNIX 和 STREAM_SOCK_STREAM。
    「本地数据报 socket 」类型是 STREAM_PF_UNIX 和 STREAM_SOCK_DGRAM。
  1. 下面我们通过stream_socket_pair() 函数创建父子进程通信例子

<?php
//创建一对连接的、不可区分的套接字流,成功时返回一个包含两个套接字资源的数组
$fd = stream_socket_pair(STREAM_PF_UNIX,STREAM_SOCK_STREAM,STREAM_IPPROTO_IP);
//$fd[0]  可用于读
//$fd[1]  可用于写

$pid = pcntl_fork();//fork 一个子进程
if($pid == 0){
   
   // $pid == 0 表示子进程执行逻辑

    while(1){
   
   // 循环 读取套接字为0的流,读取长度设置为128字节
        $data = fread($fd[0],128);

        if($data){
   
   // 如果读取到数据,使用格式化输出函数打印输出,STDOUT 表示标准化输出资源流
            fprintf(STDOUT,"收到数据:%s\n",$data);
        }

        if(strncasecmp(trim($data),'quit',4)==0){
   
   // strncasecmp 比较字符串是否相同,第三个参数表示用于比较的字符串的长度 ,如果输入的字符串是quit 就结束循环,子进程退出
            break;
        }
   }
    exit(0);
}
if($pid > 0){
   
    //pid 大于0 表示父进程执行逻辑
    while(1){
   
    // 循环读取 STDIN 表示标准输入流,接收数据的长度设置为128字节
       $data = fread(STDIN,128);
       if($data){
   
   //如果有数据,则写入$fd[1] 流,fwrite 函数第三个参数表示写入数据的长度
            fwrite($fd[1],$data,strlen($data));
       }
		
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值