先扯蛋
本来有点想删掉了【一直在草稿里呆好久了,想想还是发布吧,兴许可能有人用得上】
laravel 里的系列文章功能用得不顺手,不知道咋搞的,我就直接粗暴的粘链接了。时间原因,本来是想再深入的,想想有机会再补充吧。
玩转 PHP 网络编程全套阻塞与非阻塞IO
玩转 PHP 网络编程全套之 socket 选项设置 API
玩转 PHP 网络编程全套之数据接收与发送
玩转 PHP 网络编程全套之 unix 本地域 socket 通信
玩转 PHP 网络编程全套之 I/O 复用
玩转 PHP 网络编程全套之 socket stream 函数编程
玩转 PHP 网络编程全套之 libevent 框架首篇
玩转 PHP 网络编程全套之 libevent 框架多人聊天应用
玩转 PHP 网络编程全套之 libevent 框架之 httpServer
玩转 PHP 网络编程全套之中断系统编程
玩转 PHP 网络编程全套之多进程编程
网络框架workerman源码图文完整分析
PHP socket 手册
手册地址:https://www.php.net/manual/zh/book.sockets.php
使用需求
系统平台
php -m 或 php --ri sockets
显示如上,表示php已经支持socket扩展
几行代码先运行
$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){}
然后我php socket1.php 12345
结果显示如下
我先来解释先
Resource id #4那是PHP返回的,在PHP里称为资源
0 它是一个字符输入设备也是文件描述符【linux皆文件】
1 它是一个字符输出设备也是文件描述符
2 同样的也是
3 比较特殊它是一个epoll类型的文件,链接着一个匿名节点
4 是一个socket类型的文件,它的结点是9460457
5 是一个管道文件
实际上我去对应的目录看是没有这文件的,可以先不理它
在linux上它是这样的接下来我们继续看socket:[9460457]->4
先看它的网络状态,我们 cat net/tcp 即可
先对几个重要的选项解释一下
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查看就是这样的
所以我们socket_create后它创建了一个TCP并且处于监听状态中即LISTEN。
我们在代码的最后一句加上了while就是让它一直循环在那运行R(running),我们在该进程生成的目录下可以查看
发起一个网络连接一下看看效果
在发起连接之前我们先要执行tcpdump -i lo port 12345
然后使用telnet工具去连接,得到的结果如下
22:33:44.827309 IP localhost.37510 > localhost.italk: Flags [S], seq 1537400892
01:50:55.605018 IP localhost.italk > localhost.37510: Flags [S.], seq 1228325870, ack 1537400893
22:33:44.827328 IP localhost.37510 > localhost.italk: Flags [.], ack 1
前面的是时间,localhost.37510是客户端的ip和系统分配的随机端口
而localhost.italk是本机Flags
SYN 表示发起一个连接
ACK 一个应答
RST 重新发起连接
PSH 在传输数据时会有这个标志位
URG 紧急标志位
FIN 一个关闭报文
seq 表示源IP向目标IP传输的序列号
对端应答的时候必须在其基础上加1操作
ack 表示确认序列号,一般在seq上加1进行应答确认
以上的内容完成了大家比较熟悉的TCP连接三次握手
我想知道细节,想知道它到底在干嘛
下面我来添加几句代码,我们来更清楚的知道它们是怎么玩在一起的,是怎么进行通信的
....
while (1){
$connfd = socket_accept($sockefd);
echo $connfd.PHP_EOL;
socket_write($connfd,"hello,php 是世界上是好的语言");
}
然后输入如下命令
strace -f php socket1.php 12345
抽核心大概流程
exec
read socket1.php
create socket
exec sh
bind,listen,accept
就大概这么几句,创建socket返回文件描述符4,然后bind绑定,监听,最后呢在accept函数阻塞进程【Sleeping状态】cpu已经做其它事情了,毕竟它在”睡觉中”
接下来我们同样用 telnet 工具来连接并传输数据
接下来我们再加几句代码让其它成基本的响应给客户端
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 阻塞,有客户端连接时才会唤醒该进程来处理数据。所以务必动手实验才能领略阻塞和唤醒的感觉。