Linux进程通信之Unix套接字(一)
什么是套接字
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口
以上来自百度(实际则就是一种连接)
支持的协议
- AF_INET(IPV4)
- AF_INET6(IPV6)
- AF_UNIX(本地通讯协议,一般用于进程通信,不需要经过网卡)
套接字类型
- 流套接字(SOCK_STREAM),提供一个顺序化的、可靠的、全双工的、基于连接的字节流。支持数据传送流量控制机制。TCP 协议即基于这种流式套接字。
- 数据报套接字(SOCK_DGRAM)即提供数据报文的支持。(无连接,不可靠、固定最大长度).UDP协议即基于这种数据报文套接字。
- 原始套接字(SOCK_RAW)即提供读取原始的网络协议。这种特殊的套接字可用于手工构建任意类型的协议。一般使用这个套接字来实现 ICMP 请求(例如 ping)。
当然PHP中还封装了其他两种类型的协议,不常用(SOCK_SEQPACKET 提供一个顺序化的、可靠的、全双工的、面向连接的、固定最大长度的数据通信;数据端通过接收每一个数据段来读取整个数据包和SOCK_RDM 提供一个可靠的数据层,但不保证到达顺序。一般的操作系统都未实现此功能。)
PHP中封装了以socket开头和stream开头的两种函数,都可以实现Unix套接字通信,具体可以查看PHP官方手册
Unix套接字还分无命名的(用于父子、兄弟等有血缘关系进程通信)和命名的(任何进程都可以通信)
无命名代码示例
无命名Unix套接字通信TCP协议和UDP协议都可以使用
代码语言:javascript复制<?php
/**
* Created by PhpStorm
* User: 北溟有鱼QAQ
* Date: 2021-11-06
* Time: 15:35
* Email: 1769360227@qq.com
*/
$fd = [];
//返回两个文件描述符用于读写 $fd[0] 读 $fd[1] 写
socket_create_pair(AF_UNIX,SOCK_STREAM,0,$fd);
$pid = pcntl_fork();
//子进程接收
if($pid ==0)
{
while (1)
{
//接收数据
$data = socket_read($fd[0],128);
if($data)
{
fprintf(STDOUT,"recv--%sn",$data);
}
//判断接收到的是不是quit
if(strncasecmp($data,'quit',4) == 0)
{
break;
}
}
exit(0);
}
//父进程发送
while (1)
{
$data = fread(STDIN,128);
if($data)
{
//发送数据 MSG_EOR 表示记录标记。发送的数据完成记录。
socket_send($fd[1],$data,strlen($data),MSG_EOR);
}
if(strncasecmp($data,'quit',4) == 0)
{
break;
}
}
//回收子进程
$pid = pcntl_wait($status);
fprintf(STDOUT,"child exit pid=%dn",$pid);
执行代码,并查看打印结果,可以看到,父进程发送,子进程可以正常接收到数据
命名TCP代码示例
服务端
代码语言:javascript复制<?php
/**
* Created by PhpStorm
* User: 北溟有鱼QAQ
* Date: 2021-12-09
* Time: 17:06
* Email: 1769360227@qq.com
*/
//服务端
$file = 'unix_socket';
//创建socket
$socket = socket_create(AF_UNIX,SOCK_STREAM,0);
//绑定socket到文件
socket_bind($socket,$file);
//监听
socket_listen($socket);
//接收连接 阻塞方式,并且返回socket连接
$recv = socket_accept($socket);
if($recv)
{
while (1)
{
$data = socket_read($recv,128);
if($data)
{
fprintf(STDOUT,"recv form client %sn",$data);
socket_send($recv,$data,strlen($data),MSG_EOR);
}
if(strncasecmp($data,'quit',4) == 0)
{
exit;
}
}
}
socket_close($socket);
socket_close($recv);
客户端
代码语言:javascript复制<?php
/**
* Created by PhpStorm
* User: 北溟有鱼QAQ
* Date: 2021-12-09
* Time: 17:34
* Email: 1769360227@qq.com
*/
$file = 'unix_socket';
//创建socket
$socket = socket_create(AF_UNIX,SOCK_STREAM,0);
//连接socket
if(socket_connect($socket,$file))
{
fprintf(STDOUT,"connect okn");
while (1)
{
//开两个进程,一个发送,一个接收
$pid = pcntl_fork();
if($pid == 0)
{
$data = socket_read($socket,128);
if($data){
fprintf(STDOUT,'recv form server %s',$data);
}
if(strncasecmp($data,'quit',4) == 0)
{
exit(0);
}
}
$data = fread(STDIN,128);
if($data)
{
socket_send($socket,$data,strlen($data),MSG_EOR);
}
if(strncasecmp($data,'quit',4) == 0)
{
break;
}
}
$pid = pcntl_wait($status);
fprintf(STDOUT,"exit pid %dn",$pid);
socket_close($socket);
}
执行代码看到以下结果,不同进程之间可以进行通信
并且在执行代码时,使用strace
命令进行追踪,可以看到,在没有客户端连接时,代码则会在accept进行阻塞,当客户端连接后,代码则会在recvform进行阻塞,等待接收数据
注意
代码语言:javascript复制多次启动服务端监听文件时,会报address已被占用的错误,所以需要每次在重启的时候自行处理