转发自 公众号: PHP开源Hub
PHP socket 手册
手册地址:https://www.php.net/manual/zh/book.sockets.php
查看是否支持sockets
ubuntu@VM-154-193-ubuntu:~$ php --ri sockets
sockets
Sockets Support => enabled
socket1.php
<?php
$ip = "0.0.0.0";
$port = $argv[1];
$sockefd = socket_create(AF_INET,SOCK_STREAM,0);
echo $sockefd.PHP_EOL;
$pid = posix_getpid();
echo "pid=".$pid.PHP_EOL;
echo `ls -al /proc/{$pid}/fd`;//为了方便看该进程生成的数据
socket_bind($sockefd,$ip,$port);
socket_listen($sockefd,5);
while (1){}
ubuntu@VM-154-193-ubuntu:~/code/php$ php socket1.php 12345
Resource id #4
pid=6495
total 0
dr-x------ 2 ubuntu ubuntu 0 May 7 17:17 .
dr-xr-xr-x 9 ubuntu ubuntu 0 May 7 17:17 ..
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 0 -> /dev/pts/0
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 1 -> /dev/pts/0
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 2 -> /dev/pts/0
lr-x------ 1 ubuntu ubuntu 64 May 7 17:17 3 -> /dev/urandom
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 4 -> socket:[9343725]
lr-x------ 1 ubuntu ubuntu 64 May 7 17:17 5 -> pipe:[9343726]
Resource id #4那是PHP返回的,在PHP里称为资源
0 它是一个字符输入设备也是文件描述符【linux皆文件】
1 它是一个字符输出设备也是文件描述符
2 同样的也是
3 比较特殊它是一个epoll类型的文件,链接着一个匿名节点
4 是一个socket类型的文件,它的结点是9460457
5 是一个管道文件
实际上我去对应的目录看是没有这文件的,可以先不理它
在linux上它是这样的
ubuntu@VM-154-193-ubuntu:~$ cd /proc/6495/fd
ubuntu@VM-154-193-ubuntu:/proc/6495/fd$ ll
total 0
dr-x------ 2 ubuntu ubuntu 0 May 7 17:17 ./
dr-xr-xr-x 9 ubuntu ubuntu 0 May 7 17:17 ../
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 0 -> /dev/pts/0
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 1 -> /dev/pts/0
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 2 -> /dev/pts/0
lr-x------ 1 ubuntu ubuntu 64 May 7 17:17 3 -> /dev/urandom
lrwx------ 1 ubuntu ubuntu 64 May 7 17:17 4 -> socket:[9343725]
接下来看 4 -> socket:[9343725]
先看它的网络状态 cat net/tcp 即可
ubuntu@VM-154-193-ubuntu:/proc/6495$ cat net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 0100007F:6807 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19653 1 0000000000000000 100 0 0 10 0
1: 0100007F:6989 00000000:0000 0A 00000000:00000000 00:00000000 00000000 116 0 1578199 1 0000000000000000 100 0 0 10 0
2: 0100007F:024B 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19677 1 0000000000000000 100 0 0 10 0
3: 0100007F:18EB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 113 0 18637 1 0000000000000000 100 0 0 10 0
4: 00000000:7350 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18398 1 0000000000000000 100 0 0 10 0
5: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18395 9 0000000000000000 100 0 0 10 0
6: 00000000:2253 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18396 1 0000000000000000 100 0 0 10 0
7: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18143 12 0000000000000000 100 0 0 10 0
8: 00000000:1F98 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18401 1 0000000000000000 100 0 0 10 0
9: 0100007F:0BB8 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16481 1 0000000000000000 100 0 0 10 0
10: 00000000:3039 00000000:0000 0A 00000000:00000000 00:00000000 00000000 500 0 9343725 1 0000000000000000 100 0 0 10 0
11: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19676 1 0000000000000000 100 0 0 10 0
12: 00000000:4FDB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18402 1 0000000000000000 100 0 0 10 0
13: 00000000:01BB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18400 9 0000000000000000 100 0 0 10 0
14: 00000000:231D 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18399 1 0000000000000000 100 0 0 10 0
15: 00000000:733D 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18397 1 0000000000000000 100 0 0 10 0
16: C19A8B0A:01BB 2BCE20A8:E3CD 03 00000000:00000000 01:00000586 00000005 0 0 0 0 0000000000000000
17: C19A8B0A:0050 909E2204:994D 03 00000000:00000000 01:00000092 00000003 0 0 0 0 0000000000000000
18: C19A8B0A:0016 D574926F:55EF 03 00000000:00000000 01:00000381 00000004 0 0 0 0 0000000000000000
19: C19A8B0A:01BB 111D9F22:C0F1 03 00000000:00000000 01:0000031B 00000003 0 0 0 0 0000000000000000
20: C19A8B0A:0016 0F939F95:140D 03 00000000:00000000 01:0000059A 00000004 0 0 0 0 0000000000000000
21: C19A8B0A:0050 04544849:9E36 03 00000000:00000000 01:000001DE 00000005 0 0 0 0 0000000000000000
22: C19A8B0A:0050 6DE75017:F995 03 00000000:00000000 01:00000854 00000005 0 0 0 0 0000000000000000
23: C19A8B0A:0016 123996B6:2468 01 00000000:00000000 02:0000004A 00000000 0 0 9335610 2 0000000000000000 24 4 10 10 9
24: C19A8B0A:0016 4E97F18F:05DE 03 00000000:00000000 01:000000D9 00000002 0 0 0 0 0000000000000000
25: C19A8B0A:FCC0 A64B350A:15C6 01 00000000:00000000 00:00000000 00000000 0 0 19765 1 0000000000000000 24 4 30 10 -1
26: C19A8B0A:0016 0A779D68:43FE 03 00000000:00000000 01:00000372 00000004 0 0 0 0 0000000000000000
27: C19A8B0A:0050 9CE7F430:A57F 03 00000000:00000000 01:00000806 00000005 0 0 0 0 0000000000000000
28: C19A8B0A:0050 9F9F9F95:A750 03 00000000:00000000 01:00000194 00000003 0 0 0 0 0000000000000000
29: C19A8B0A:0016 53C77E8D:40EA 03 00000000:00000000 01:00000A8E 00000005 0 0 0 0 0000000000000000
30: C19A8B0A:0016 18BFE44E:EF6A 03 00000000:00000000 01:000005F8 00000005 0 0 0 0 0000000000000000
31: C19A8B0A:0016 123996B6:26A8 01 00001A90:00000000 01:00000007 00000000 0 0 9344811 4 0000000000000000 23 6 5 10 9
32: C19A8B0A:01BB 05BA409E:7E9F 03 00000000:00000000 01:000000A2 00000002 0 0 0 0 0000000000000000
33: C19A8B0A:0016 07AA519E:8E34 03 00000000:00000000 01:00000551 00000004 0 0 0 0 0000000000000000
34: C19A8B0A:0050 47BBFAAB:057C 03 00000000:00000000 01:00000743 00000005 0 0 0 0 0000000000000000
35: C19A8B0A:01BB 0D3D90B7:1DFB 03 00000000:00000000 01:000008B1 00000005 0 0 0 0 0000000000000000
36: C19A8B0A:01BB 06114EC9:A4E0 03 00000000:00000000 01:000004EA 00000004 0 0 0 0 0000000000000000
37: C19A8B0A:0050 1B812785:F552 03 00000000:00000000 01:00000165 00000002 0 0 0 0 0000000000000000
38: C19A8B0A:01BB CF0A5E8F:FB39 03 00000000:00000000 01:0000008A 00000001 0 0 0 0 0000000000000000
39: C19A8B0A:01BB 052B0EBA:ACB1 03 00000000:00000000 01:00000457 00000005 0 0 0 0 0000000000000000
40: C19A8B0A:0050 73EE74B0:C0BF 03 00000000:00000000 01:0000056C 00000005 0 0 0 0 0000000000000000
41: C19A8B0A:0016 F068C8A7:703A 03 00000000:00000000 01:000003E6 00000004 0 0 0 0 0000000000000000
42: C19A8B0A:01BB 15136F73:6DB7 03 00000000:00000000 01:00000144 00000004 0 0 0 0 0000000000000000
43: C19A8B0A:0016 7AF5430D:F26A 03 00000000:00000000 01:000000D3 00000003 0 0 0 0 0000000000000000
44: C19A8B0A:0016 6756632D:7693 03 00000000:00000000 01:0000004E 00000001 0 0 0 0 0000000000000000
45: C19A8B0A:0016 64851591:21E4 03 00000000:00000000 01:000002D5 00000004 0 0 0 0 0000000000000000
先对几个重要的选项解释一下
local_address 表示本地ip和端口号用16进制表示
rem_address 远程服务器ip和端口
st 表示套接字状态
1 TCP_ESTABLISHED
2 TCP_SYN_SENT
3 TCP_SYN_RECV
4 TCP_FIN_WAIT1
5 TCP_FIN_WAIT2
6 TCP_TIME_WAIT
7 TCP_CLOSE
8 TCP_CLOSE_WAIT
9 TCP_LAST_ACK
10 TCP_LISTEN 16进制就是A
11 TCP_CLOSING
tx_queue 发送队列中的数据长度
rx_queue 接收的数据长度
tr 定时器类型
0 未启动定时
1 开启socket 定时重传机制
2 开启持续定时
3 开启连接定时器 FIN_WAIT2定时
4 TIME_WAIT定时
tm->when 超时时间
retrnsmt 超时重传次数
uid 用户的id
inode socket套接字对应的结点inode
大家都了解TCP数据流拥有自己的接收缓冲区和发送缓冲区,同时呢具有超时重传机制和数据应答确诊机制
这个socket它的状态用netstat -antp查看就是这样的
ubuntu@VM-154-193-ubuntu:/proc/6495$ sudo netstat -antp | grep 12345
tcp 0 0 0.0.0.0:12345 0.0.0.0:* LISTEN 6495/php
可以看到 12345 在监听
我们在代码的最后一句加上了while就是让它一直循环在那运行R(running),我们在该进程生成的目录下可以查看
ubuntu@VM-154-193-ubuntu:/proc/6495$ cat status
Name: php
State: R (running)
Tgid: 6495
Ngid: 0
Pid: 6495
PPid: 19018
TracerPid: 0
Uid: 500 500 500 500
Gid: 500 500 500 500
FDSize: 256
Groups: 4 24 27 30 46 110 115 116 123 500
NStgid: 6495
NSpid: 6495
NSpgid: 6495
NSsid: 19018
VmPeak: 322904 kB
VmSize: 322900 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 30988 kB
VmRSS: 30988 kB
VmData: 6988 kB
VmStk: 132 kB
VmExe: 4180 kB
VmLib: 67400 kB
VmPTE: 568 kB
VmPMD: 16 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
Threads: 1
SigQ: 0/7320
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000184004a07
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Seccomp: 0
Speculation_Store_Bypass: vulnerable
Cpus_allowed: 1
Cpus_allowed_list: 0
Mems_allowed: 00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 3
nonvoluntary_ctxt_switches: 153287
现在我们发起一个网络连接看看效果
在发起连接之前我们先用tcpdump抓包
tcpdump -i lo port 12345
然后使用telnet工具去连接 telnet 127.0.0.1 12345 ,得到的结果如下
10:49:26.539984 IP localhost.localdomain.16494 > localhost.localdomain.12345: Flags [S], seq 3577382355, win 43690, options [mss 65495,sackOK,TS val 169282630 ecr 0,nop,wscale 9], length 0
10:49:26.540001 IP localhost.localdomain.12345 > localhost.localdomain.16494: Flags [S.], seq 2656930451, ack 3577382356, win 43690, options [mss 65495,sackOK,TS val 169282630 ecr 169282630,nop,wscale 9], length 0
10:49:26.540012 IP localhost.localdomain.16494 > localhost.localdomain.12345: Flags [.], ack 1, win 86, options [nop,nop,TS val 169282630 ecr 169282630], length 0
Flags
- SYN 表示发起一个连接
- ACK 一个应答
- RST 重新发起连接
- PSH 在传输数据时会有这个标志位
- URG 紧急标志位
- FIN 一个关闭报文
seq 表示源IP向目标IP传输的序列号
对端应答的时候必须在其基础上加1操作
ack 表示确认序列号,一般在seq上加1进行应答确认
以上的内容完成了大家比较熟悉的TCP连接三次握手
使用 strace 来看看系统调用
先添加几行代码
while (1){
$connfd = socket_accept($sockefd);
echo $connfd.PHP_EOL;
socket_write($connfd,"hello,php 是世界上是好的语言");
}
然后执行命令
strace -f php socket1.php 12345
结果如下(核心流程):
execve("/usr/bin/php", ["php", "socket1.php", "12345"], [/* 14 vars */]) = 0
...
open("socket1.php", O_RDONLY) = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=441, ...}) = 0
read(4, "<?php\n$ip = \"0.0.0.0\";\n$port = $"..., 4096) = 441
lseek(4, 0, SEEK_SET) = 0
...
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 4
write(1, "Resource id #4\n", 15Resource id #4
) = 15
write(1, "pid=10327\n", 10pid=10327
) = 10
pipe2([5, 6], O_CLOEXEC) = 0
...
execve("/bin/ls", ["ls", "-al", "/proc/10327/fd"], [/* 17 vars */]) = 0
...
bind(4, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(4, 5) = 0
accept(4,
就大概这么几句,创建socket返回文件描述符4,然后bind绑定,监听,最后呢在accept函数阻塞进程【Sleeping状态】cpu已经做其它事情了,毕竟它在”睡觉中”
ubuntu@VM-154-193-ubuntu:/proc/10327$ cat status
Name: php
State: S (sleeping)
Tgid: 10327
Ngid: 0
Pid: 10327
PPid: 10325
TracerPid: 10325
Uid: 0 0 0 0
Gid: 0 0 0 0
...
接下来同样用 telnet 来连接并传输数据
ubuntu@VM-154-193-ubuntu:~$ telnet 127.0.0.1 12345
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello,php 是世界上是好的语言
ubuntu@VM-154-193-ubuntu:/proc/10327$ cat net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 0100007F:6807 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19653 1 0000000000000000 100 0 0 10 0
1: 0100007F:6989 00000000:0000 0A 00000000:00000000 00:00000000 00000000 116 0 1578199 1 0000000000000000 100 0 0 10 0
2: 0100007F:024B 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19677 1 0000000000000000 100 0 0 10 0
3: 0100007F:18EB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 113 0 18637 1 0000000000000000 100 0 0 10 0
4: 00000000:7350 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18398 1 0000000000000000 100 0 0 10 0
5: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18395 9 0000000000000000 100 0 0 10 0
6: 00000000:2253 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18396 1 0000000000000000 100 0 0 10 0
7: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18143 7 0000000000000000 100 0 0 10 0
8: 00000000:1F98 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18401 1 0000000000000000 100 0 0 10 0
9: 0100007F:0BB8 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16481 1 0000000000000000 100 0 0 10 0
10: 00000000:3039 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10061459 1 0000000000000000 100 0 0 10 0
11: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19676 1 0000000000000000 100 0 0 10 0
12: 00000000:4FDB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18402 1 0000000000000000 100 0 0 10 0
13: 00000000:01BB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18400 5 0000000000000000 100 0 0 10 0
14: 00000000:231D 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18399 1 0000000000000000 100 0 0 10 0
15: 00000000:733D 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 18397 1 0000000000000000 100 0 0 10 0
16: C19A8B0A:0050 BF28FDAB:4FBE 03 00000000:00000000 01:0000044E 00000004 0 0 0 0 0000000000000000
17: C19A8B0A:0016 E14AF9A3:5AB8 03 00000000:00000000 01:00000B54 00000005 0 0 0 0 0000000000000000
18: C19A8B0A:0016 4E54A2D2:86D5 03 00000000:00000000 01:0000010E 00000003 0 0 0 0 0000000000000000
19: C19A8B0A:0016 7B85F28B:F314 03 00000000:00000000 01:000004C6 00000005 0 0 0 0 0000000000000000
20: 0100007F:3039 0100007F:B8C8 01 00000000:00000000 00:00000000 00000000 0 0 10061473 1 0000000000000000 20 0 0 10 -1
21: C19A8B0A:01BB 7322E742:F4F7 03 00000000:00000000 01:00000804 00000005 0 0 0 0 0000000000000000
22: C19A8B0A:01BB 58C3CB31:5175 03 00000000:00000000 01:000004C0 00000005 0 0 0 0 0000000000000000
23: 0100007F:B8C8 0100007F:3039 01 00000000:00000000 00:00000000 00000000 500 0 10182911 1 0000000000000000 20 4 30 10 -1
24: C19A8B0A:0016 123996B6:14A3 01 00000000:00000000 02:0000000C 00000000 0 0 10048465 2 0000000000000000 22 4 0 10 7
25: C19A8B0A:FCC0 A64B350A:15C6 01 00000000:00000000 00:00000000 00000000 0 0 19765 1 0000000000000000 23 4 30 10 -1
26: C19A8B0A:0016 C2BDF1C0:AFF0 01 00000000:00000000 02:00000060 00000000 0 1 10183729 2 0000000000000000 72 4 19 10 7
27: C19A8B0A:0050 9E6FB29B:F86E 03 00000000:00000000 01:00000432 00000004 0 0 0 0 0000000000000000
28: C19A8B0A:0050 B1201298:BA78 03 00000000:00000000 01:000004AC 00000004 0 0 0 0 0000000000000000
29: C19A8B0A:0050 385F0F71:E3E0 03 00000000:00000000 01:00000312 00000004 0 0 0 0 0000000000000000
30: C19A8B0A:0050 865B3BA5:8C6D 03 00000000:00000000 01:000000A0 00000001 0 0 0 0 0000000000000000
31: C19A8B0A:0016 D1E9A0A0:4952 03 00000000:00000000 01:00000459 00000004 0 0 0 0 0000000000000000
32: C19A8B0A:01BB C00BDB94:5178 03 00000000:00000000 01:0000038D 00000005 0 0 0 0 0000000000000000
33: C19A8B0A:0050 0AF88FD6:BF23 03 00000000:00000000 01:000007E3 00000005 0 0 0 0 0000000000000000
34: C19A8B0A:0050 1D0E8675:7C82 03 00000000:00000000 01:00000539 00000005 0 0 0 0 0000000000000000
35: C19A8B0A:0016 E0EC07A9:E4DB 03 00000000:00000000 01:00000408 00000005 0 0 0 0 0000000000000000
36: C19A8B0A:0016 123996B6:109E 01 00000000:00000000 02:00000081 00000000 0 0 10037564 2 0000000000000000 21 4 0 2 2
37: C19A8B0A:0050 D3C34C35:2245 03 00000000:00000000 01:000001B2 00000005 0 0 0 0 0000000000000000
38: C19A8B0A:0016 B8FBE91B:A964 03 00000000:00000000 01:00000B76 00000005 0 0 0 0 0000000000000000
39: C19A8B0A:01BB 4329A528:9813 03 00000000:00000000 01:00000C6D 00000005 0 0 0 0 0000000000000000
40: C19A8B0A:0016 123996B6:1270 01 00001A90:00000000 01:00000008 00000000 0 0 10045797 4 0000000000000000 24 4 3 10 7
while (1){
$connfd = socket_accept($sockefd);
echo $connfd.PHP_EOL;
socket_write($connfd,"hello,php 是世界上是好的语言");
echo socket_read($connfd,4098,PHP_BINARY_READ);
}
我们看到 php 语言低层调用了 libc API【任何语言都是一样】
都是先创建一个 socketfd ,再把端口和地址绑架到它身上,再 listen 进行监听,直到 accept 阻塞,有客户端连接时才会唤醒该进程来处理数据。所以务必动手实验才能领略阻塞和唤醒的感觉。