php socket网络编程基础知识(二):socket函数
说明
- 我们都知道通过IP,端口等可以实现两台机器之间的数据互通,但具体要怎么操作,系统给我们提供了socket接口,通过调用socket函数就可以实现互通。
- php的socket扩展和C本身的非常相似,如果找不到php相关的资料,可以对照着C的socket函数来学习,例如:C语言SOCKET编程指南
- php的socket文档,文档中有很多函数,我们只找主要通讯流程的函数理解其流程,其它函数后期用到再去查看即可
通信流程
图片引用自sockets百度百科词条,tcp通信的流程,其它方式的通信可参考
相关函数
-
服务端相关函数
-
socket_create
( int $domain , int $type , int $protocol ) : resource- 创建一个socket,例如
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
- $domain是选择
IP4
或者IP6
或者UNIX本地通讯
,配置过nginx的话应该会知道参数fastcgi_pass
用来连接php-fpm的,有两种方式,一种是tcp,一种是unix socket,就是对应这里的IP方式和UNIX本地方式 - $type是
SOCK_STREAM
(tcp)或者SOCK_DGRAM
(udp)或者其它 - $protocol 一般为0是IP协议。
- 上方语句创建了ip4地址的tpc套接字,更多参数自行查看文档。
- 创建一个socket,例如
-
socket_bind
( resource $socket , string $address [, int $port = 0 ] ) : bool- 将上方创建的socket绑定具体的IP和端口,到时候客户端连接需要填写此IP和接口
-
socket_listen
( resource $socket [, int $backlog = 0 ] ) : bool- 监听客户端的链接。(udp通信时,不需要用到此函数)
- 第二个参数backlog是accept之前握手队列的大小,如果我们配置过php-fpm的话应该有接触过,叫
listen.backlog
,这个参数就对应着socket_listen中的第二个参数,目前php-fpm默认值是511,而workerman中默认值是102400 - 相关参考:socket_listen里面第二个参数backlog的用处;TCP SOCKET中backlog参数的用途是什么?
-
socket_accept
( resource $socket ) : resource- 接收客户端连接(udp通信时,不需要用到此函数)
至此就可以和客户端联通,进行读写操作了,需要注意的是其返回值,是一个新的socket,而后续读写是在这个新的socket下进行的而不是一开始创建的socket。
- 接收客户端连接(udp通信时,不需要用到此函数)
-
-
客户端相关函数
socket_connect
( resource $socket , string $address [, int $port = 0 ] ) : bool- 客户端连接服务端,同服务端一样,首先要调用
socket_create
创建一个socket,然后再调用此函数连接到服务端
- 客户端连接服务端,同服务端一样,首先要调用
-
读写函数
socket_send
( resource $socket , string $buf , int $len , int $flags ) : int- 发送数据给socket
socket_write
( resource $socket , string $buffer [, int $length = 0 ] ) : int- 向socket中写数据
- 上两个函数基本一样,socket_send中最后一位$flags参数设置为0,就等于socket_write,网上关于这两个函数的比较非常少,基本搜索不到,而且费解的是查看php源码会发现这两个方法内部有些许差异,
socket_write
中引用#ifndef PHP_WIN32
来判定如果是非windows系统,则调用系统的write
方法来实现,windows系统则调用系统的send
方法来实现,但是socket_send
则没有判定,统一调用系统的send
来实现,不明白socket_write
为什么要增加判定? socket_sendto
( resource $socket , string $buf , int $len , int $flags , string $addr [, int $port = 0 ] ) : int- 主要是udp通信时,发送数据
socket_recv
( resource $socket , string &$buf , int $len , int $flags ) : int- 从socket中接收数据,需要注意的是返回值是int表示接收的字节数,而内容存在第二个引用类型的参数$buf中
socket_read
( resource $socket , int $length [, int $type = PHP_BINARY_READ ] ) : string- 从socket中读数据,返回结果就是读的数据
- 上面两个函数也很相近,除了返回值的区别,socket_recv最后$flags为0和socket_read最后$type为PHP_BINARY_READ时读取的数据是一样的,但是如果flags或者type变化,则不一定一样了。
socket_recvfrom
( resource $socket , string &$buf , int $len , int $flags , string &$name [, int &$port ] ) : int- 主要是udp通信是,接收数据
-
其它常用函数
socket_close
( resource $socket ) : void- 关闭socket
socket_set_nonblock
( resource $socket ) : bool- 设置为非阻塞
socket_set_block
( resource $socket ) : bool- 设置为阻塞
- 上面两个影响的函数为:
socket_connect
、socket_accept
以及上方的各种读写函数
,以socket_accept
为例,阻塞就是调用此方法后,如果没有接受到客户端,则此方法一直卡住,等待客户端加入后才会进行下一步动作,而非阻塞则是如果没有客户端加入,则直接返回false,具体的会在后面的IO模型中总结 socket_select
( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] ) : int- 调用系统select()相关IO模型
- 详细的在后面的IO模型中会总结
示例
- 服务端socket_service.php:
123456789101112131415161718192021222324252627282930313233343536
<?php
//IP和端口
$address
=
'127.0.0.1'
;
$port
= 8888;
//创建
$listenSocket
= socket_create(AF_INET, SOCK_STREAM, 0);
//绑定
socket_bind(
$listenSocket
,
$address
,
$port
);
//监听
socket_listen(
$listenSocket
, 5);
//循环接入客户端
while
(true) {
//接入客户端
$connectSocket
= socket_accept(
$listenSocket
);
$msg
=
"hello\r\n"
;
//发送给客户端,注意此时用的是$connectSocket,而不是$listenSocket
socket_write(
$connectSocket
,
$msg
,
strlen
(
$msg
));
//关闭
socket_close(
$connectSocket
);
}
//关闭
socket_close(
$listenSocket
);
客户端socket_client.php:
<?php
//服务端IP和端口
$address
=
'127.0.0.1'
;
$port
= 8888;
//创建
$socket
= socket_create(AF_INET, SOCK_STREAM, 0);
//连接
$result
= socket_connect(
$socket
,
$address
,
$port
);
//读取
$out
= socket_read(
$socket
, 2048);
echo
$out
;
//关闭
socket_close(
$socket
);
- 上方是简单的示例代码,在命令行cli模式下运行
php socket_service.php
,然后再新起一窗口运行php socket_client.php
就能看到客户端联通后收到了服务端发送的数据并显示出来。 - 我们在普通的编程中,通常是尽量避免使用while(true)的,害怕无限循环会导致卡死,但在网络编程中会经常用到,其实只要控制好就没问题,上方之所以可以用是因为默认socket_accept是阻塞的,也就是没有客户端接入时会卡在这里等待接入,所以不会造成性能影响。
- 也可以查看php文档中的示例,按照提示来测试。
- 通过示例我们可以看到是可以连通,但是有很大的局限性,只能一个服务端连接一个客户端,如果想同时连接多个客户端是不行的(上方简单的代码可能不明显,毕竟连接后接着就关闭断开了,但是运行php文档中的例子会比较明显,同时打开几个窗口用
telnet
来链接,会发现只有一个结束后,另一个才能接入),解决方法就是可以用fork子进程或者是IO多路复用,后续会总结。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!