目录
PHP socket
在那么多语言里面,PHP 最厉害的地方就在于它众多功能强大的内建函数,对于 socket 的实现也是基于函数,实现了 socket 通讯功能的底层接口,编程之前需要在配置文件(php.ini)里开启 socket 功能,在 php-7.x 的配置文件里搜索extension=sockets,然后把前面的注释 “;” 去掉,保存退出就可以了。PHP 的 socket 编程流程和标准的 socket 编程步骤一样,服务端需要创建套接字,然后绑定端口,开启监听;客户端要先创建套接字才能开始连接。
下面是 PHP 实现 socket 功能的内建函数:
函数 | 参数 | 作用 |
---|---|---|
socket_accept() | socket=套接字对象 | 与客户端进行连接 |
socket_bind() | socket=套接字对象;address=服务器IP地址(不能是localhost);port=端口 | 服务端绑定套接字、ip、端口 |
socket_clear_error() | socket=套接字对象 | 清除套接字或上次错误代码上的错误 |
socket_close() | socket=套接字对象 | 关闭套接字 |
socket_connect() | socket=套接字对象;address=服务器IP地址;port=端口 | 客户端主动连接服务端 |
socket_create_listen() | port=端口;backlog=最大等待连接数 | 在给定端口监听新的AF_INET连接,简化了创建和绑定套接字的流程 |
socket_create_pair() | domain=套接字类型;type=是否面向连接;protocol=协议类型;&fd=放入两个套接字资源的数组 | 创建两个一样套接字,并存储在&fd中,通常用于IPC(进程间的通信) |
socket_create() | domain=套接字类型;type=是否面向连接;protocol=协议类型 | 创建套接字对象 |
socket_get_option() | socket=套接字对象;level=指定协议级别(用GetProtoByname()函数可获取协议编号);optname=指定设置选项,详见下面的开发者手册 | 获取套接字对象的配置选项 |
socket_getpeername() | socket=套接字对象;&address=目标服务器地址;&port=查询目标的端口 | 查询连接目标的套接字,用于确定本机发送的套接字类型 |
socket_getsockname() | socket=套接字对象;&addr=本机地址;&port=本机端口 | 作用同上,查询本地的套接字 |
socket_last_error() | socket=套接字对象 | 返回套接字上的最后一个错误 |
socket_listen() | socket=套接字对象 | 服务端开启监听 |
socket_read() | socket=套接字对象;length=读取字节的长度 | 从套接字读取一定长度的信息 |
socket_recv() | socket=套接字对象;buf=接收数据保存的变量;len=接收的数据长度;flags=指定标志来控制如何接收数据 | 从已连接的套接字接收数据,对应send |
socket_recvfrom() | socket=套接字对象;buf=接收数据保存的变量;len=接收的数据长度;flags=指定标志来控制如何接收数据;name=套接字为AF_UNIX,名称为文件路径。套接字为AF_INET/未导向,名称为IP地址;port=接收数据的端口 | 可用于接收UDP数据(面向连接/非连接),对应sendto |
socket_select() | read=指定可读性检查的套接字数组;write=指定可写性检查的套接字数组;except=指定错误检查的套接字数组;tv_sec=函数的等待时间(秒/s);tv_usec=函数的等待时间(微秒/μs) | 用数组存放套接字阵列,通过这个函数供系统调用,可指定超时 |
socket_send() | socket=套接字对象;buf=发送的内容;len=发送的数据长度;flags=指定标志来控制如何发送数据 | 向已连接的套接字发送数据 |
socket_sendto() | socket=套接字对象;buf=存储着要发送的数据的变量;len=发送数据的长度;flags=控制数据发送的行为;addr=目标IP地址 | 可用于发送UDP数据(面向连接/非连接) |
socket_set_block() | socket=套接字对象 | 在套接字对象上设置阻塞模式(tcp) |
socket_set_nonblock() | socket=套接字对象 | 在套接字对象上设置非阻塞模式(udp) |
socket_set_option() | socket=套接字对象;level=指定协议级别(用GetProtoByname()函数可获取协议编号);optname=指定设置选项,详见下面的开发者手册;optval=对应的选项值 | 设置套接字对象选项 |
socket_shutdown() | socket=套接字对象;how=0/1/2(分别对应停止读取/写入/读取+写入) | 立即停止socket数据的发送/接收 |
socket_strerror() | errno=有效的套接字错误号,可由socket_last_error()生成 | 返回当前套接字的报错信息 |
socket_write() | socket=套接字对象;buffer=写入内容;length=写入的缓冲区长度(可选) | 发送信息 |
附:开发者文档
在 PHP 的 socket_create() 函数里,每个参数常量(这些常量的概念在其他语言中也通用)有一个对应的整型标号,输入函数参数的时候可用这个整数代替:
domain | 对应标号 | 描述 |
---|---|---|
AF_UNIX | 1 | 本地通讯协议,用于进程间通讯 |
AF_INET | 2 | IPV4协议 |
AF_INET6 | 10 | IPV6协议 |
type | 对应标号 | 描述 |
---|---|---|
SOCK_STREAM | 1 | 面向连接的类型 |
SOCK_DGRAM | 2 | 面向无连接的类型 |
SOCK_RAW | 3 | 提供读取原始的网络协议,可构造任意类型的协议 |
SOCK_RDM | 4 | 提供一个可靠数据层,但不保证到达顺序 |
SOCK_SEQPACKET | 5 | 面向连接的类型,只支持IPX协议 |
protocol | 对应标号 | 描述 |
---|---|---|
SOL_ TCP | 6 | tcp协议 |
SOL_UDP | 17 | udp协议 |
… | … | … |
PHP 的 socket 编程和其他语言一样,也支持多种协议,这里以 TCP/UDP 为例进行介绍。在实现功能这块,PHP 和其他语言一样也是 C/S 架构,但是实现过程理解起来比编译型语言更加简单,这是因为 PHP 只用相关的内建函数拼在一起就能够实现 socket 通信了。
如上图所示,每一个函数对应着 socket 编程的一个步骤,甚至有多个函数可供选择,只要懂得 socket 原理就可以很快上手,非常容易理解。
下面通过对应流程图和简单的例子来进行说明:
socket_*-TCP
首先是使用 socket_* 系列的函数,编程流程如下:
可以看见每个 socket 编程的步骤都有对应的函数,在数据收发的功能上甚至还有 send/recv、read/write 两种方式,其中 shutdown() 函数作用在信息发送过程中,socket_create_listen() 函数是对 socket() → bind() → listen() 步骤的封装,但是仅限于 AF_INET+SOL_TCP 类型的套接字。例子:
客户端
<?php
/**
*这是一个socket-TCP的客户端,connection方法用来和服务端建立连接,
1. 并从命令行接受用户输入发送给服务端。
2. 编程步骤如下:
3. 1.创建套接字对象 socket_create
4. 2.获取服务端套接字配置项(可选) sock_get_option
5. 3.主动连接服务器 socket_connect
6. 4.数据发送 socket_write
7. 5.关闭套接字 socket_close
*/
class Customer
{
private $host='127.0.0.1';
private $port=8888;
public function connection(){
$sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
print socket_get_option($sock,SOL_SOCKET,SO_REUSEADDR).PHP_EOL;
if(!socket_connect($sock,$this->host,$this->port)){
echo '连接失败 '.socket_last_error().PHP_EOL;}
else{
echo '-----连接成功-----'.PHP_EOL;
while(true){
$data=fgets(STDIN);
socket_write($sock,$data,1024);
if (trim($data)=='q'){
socket_close($sock);
break;
}
}
}
}
}
服务端
<?php
/**
8. 这是一个socket-TCP服务端,通过start方法启动,sockListener方法用来开启监听和建立连接,sockHandler方法接收数据
9. 如果用socket_create_listen()则要注释掉socket_create、socket_bind、socket_listen
10. 编程步骤如下:
11. 1.创建套接字对象 $sock
12. 2.设置套接字配置项(可选) socket_set_option
13. 2.绑定套接字、服务器地址、端口 socket_bind
14. 3.开启监听 socket_listen(),并接受连接 socket_accept
15. 4.数据接收 socket_read
16. 5.关闭连接 socket_close
*/
class Server
{
private $host='127.0.0.1';
private $port=8888;
public function sockListener(){
//$sock=socket_create_listen($this->port,10);
$sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_set_option($sock,SOL_SOCKET,SO_REUSEADDR,1);
if(!socket_bind($sock,$this->host,$this->port)){
echo '绑定失败'.PHP_EOL;}
if(!socket_listen($sock,4)){
echo '监听出错'.PHP_EOL;}
else{
echo '监听中...'.PHP_EOL;}
return socket_accept($sock);
}
public function sockHandler($conn){
$data=socket_read($conn,1024);
echo $data;
if (trim(