1 Socket简介

Socket 的官方解释: 在网络编程中最常用的方案便是Client/Server(客户机/服务器)模型。在这种方案中客户应用程序向服务器程序请求服务。一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一 直处于休眠状态,直到一个客户向这个服务的地址提出了连接请求。在这个时刻,服务程序被"惊醒"并且为客户提供服务-对客户的请求作出适当的反应。为了方便这种Client/Server模型的网络编程,90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即WindowsSockets规范,它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。现在的Winsock已经基本上实现了与协议无关,你可以使用Winsock来调用多种协议的功能,但较常使用的是TCP/IP协议。Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现

我们可以简单的把 Socket 理解为一个可以连通网络上不同计算机应用程序之间的管道,把一堆数据从管道的 A 端扔进去,则会从管道的 B 端(同时还可以从C、D、E、F……端冒出来)。

2 Socket 函数介绍

Socket 通信依次会进行 Socket 创建、Socket 监听、Socket 收发、Socket 关闭几个阶段,下面我们列举出 PHP 网络编程中最常用也是必不可少的几个常用的函数进行进一步的说明。

socket_create

//getprotobyname()函数是PHP中的内置函数,它返回指定协议名称的协议编号。
$commonProtocol=getprotobyname("tcp");
//创建一个新的 socket 资源 函数原型
$socket=socket_create (AF_INET, SOCK_STREAM,$commonProtocol);
if(!$socket){
die("错误");
}
// 将创建的 socket 资源绑定到具体的 ip 地址和端口
socket_bind($socket,"localhost",123456);

//在具体的地址下监听 socket 资源的收发操作
socket_listen($socket);
//关闭 socket 资源
socket_close($socket);

TODO : 创建一个新的 socket 资源 函数原型: ​​ socket_create ( int $domain , int $type , int $protocol )​​ 它包含三个参数,分别如下:

  • domain:AF_INET、AF_INET6、AF_UNIX,​​AF​​​的释义就 ​​address​​​ ​​family​​,地址族的意思,我们常用的有 ipv4、ipv6
  • type: SOCK_STREAM、SOCK_DGRAM等,最常用的就是​​SOCK_STREAM​​,基于字节流的SOCKET类型,也是TCP协议使用的类型
  • protocol: SOL_TCP、SOL_UDP 这个就是具体使用的传输协议,一般可靠的传输我们选择 TCP,游戏数据传输我们一般选用 UDP 协议

socket_bind

//getprotobyname()函数是PHP中的内置函数,它返回指定协议名称的协议编号。
$commonProtocol=getprotobyname("tcp");
//创建一个新的 socket 资源 函数原型
$socket=socket_create (AF_INET, SOCK_STREAM,$commonProtocol);

// 将创建的 socket 资源绑定到具体的 ip 地址和端口
socket_bind($socket,"localhost",123456);
//连接到一个ip地址
socket_connect($socket,"localhost",80);
$request='GET/HTTP/1.1'."\r\n".'Host:eduask.cn'."\r\n\r\n";

socket_write($socket,$request);
//关闭 socket 资源
socket_close($socket);

TODO : 将创建的 socket 资源绑定到具体的 ip 地址和端口 函数原型: ​​bool socket_bind ( resource $socket , string $address [, int $port = 0 ] )​

它包含三个参数,分别如下:

  • socket: 使用​​socket_create​​创建的 socket 资源,可以认为是 socket 对应的 id
  • address: ip 地址
  • port: 监听的端口号,WEB 服务器默认80端口

socket_listen
TODO : 在具体的地址下监听 socket 资源的收发操作 函数原型: ​​​bool socket_listen ( resource $socket [, int $backlog = 0 ] )​

它包含两个个参数,分别如下:

  • socket: 使用​​socket_create​​创建的socket资源
  • backlog: 等待处理连接队列的最大长度

socket_accept
TODO : 接收一个新的 socket 资源 函数原型: ​​​resource socket_accept ( resource $socket )​

  • socket: 使用socket_create创建的socket资源

socket_write
TODO : 将指定的数据发送到 对应的 socket 管道 函数原型: ​​​int socket_write ( resource $socket , string $buffer [, int $length ] )​

  • socket: 使用​​socket_create​​创建的socket资源
  • buffer: 写入到socket资源中的数据
  • length: 控制写入到​​socket​​​资源中的​​buffer​​​的长度,如果长度大于​​buffer​​​的容量,则取​​buffer​​的容量

socket_read
TODO : 获取传送的数据 函数原型: ​​​int socket_read ( resource $socket , int $length )​

socket: 使用​​socket_create​​​创建的socket资源
length: ​​​socket​​​资源中的​​buffer​​​的长度
socket_close
TODO : 关闭 socket 资源 函数原型: ​​​void socket_close ( resource $socket )​

socket: ​​socket_accept​​​或者​​socket_create​​​产生的资源,不能用于​​stream​​资源的关闭

stream_socket_server

由于创建一个SOCKET的流程总是 socket、bind、listen,所以PHP提供了一个非常方便的函数一次性创建、绑定端口、监听端口

函数原型: ​​resource stream_socket_server ( string $local_socket [, int &$errno [, string &$errstr [, int $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN [, resource $context ]]]] )​

  • local_socket: 协议名://地址:端口号
  • errno: 错误码
  • errstr: 错误信息
  • flags: 只使用该函数的部分功能
  • context: 使用​​stream_context_create​​​函数创建的资源流上下文
    socket_clear_error

清除socket的错误
socket_connect

创建一个客户端到一个端口或者连接上

3 socket文件编辑

基于上面的函数我们可以很方便的去构建一个 socket 通信程序,我们在​​/home/shiyanlou/​​​目录下去编辑一个 ​​server.php​​,如下:

<?php 

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

socket_bind($sock, "127.0.0.1", 8080);

socket_listen($sock);

for ( ; ; ) {
$conn = socket_accept($sock);

$write_buffer = "HTTP/1.0 200 OK\r\nServer: my_server\r\nContent-Type: text/html; charset=utf-8\r\n\r\nhello!world";

socket_write($conn, $write_buffer);

socket_close($conn);
}

运行:

sudo service php7.2-fpm start;
php server.php

运行成功后,打开浏览器,输入http://127.0.0.1:8080,回车看到结果hello!world,同时,我们也可以使用集成函数stream_socket_server去改写成:

<?php 

$sock = stream_socket_server("tcp://127.0.0.1:8080", $errno, $errstr);

for ( ; ; ) {
$conn = stream_socket_accept($sock);

$write_buffer = "HTTP/1.0 200 OK\r\nServer: my_server\r\nContent-Type: text/html; charset=utf-8\r\n\r\nhello!world";

fwrite($conn, $write_buffer);

fclose($conn);
}

需要注意的是,这里不能使用​​socket_accept​​​,因为​​stream_socket_server​​​和​​socket_create​​​创建的不是同一种资源,​​stream_socket_server​​​创建的是stream资源,这也是为什么可以用​​fwrite​​​、​​fread​​​、​​fclose​​​操作该资源的原因. 而​​socket_create​​​创建的是socket资源,而不是stream资源,所以​​socket_create​​​创建的资源只能用​​socket_write​​​、​​socket_read​​​、​​socket_close​​来操作.

这章我们介绍了一些最常用的socket相关函数,值得注意的是​​stream_socket_server​​​和​​socket_create​​创建的资源不是同一种类型,针对这些资源的函数不能混用。