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_bind ( resource $socket , string $address [, int $port = 0 ] ) : bool

      • 将上方创建的socket绑定具体的IP和端口,到时候客户端连接需要填写此IP和接口
    • socket_listen ( resource $socket [, int $backlog = 0 ] ) : bool

    • socket_accept ( resource $socket ) : resource

      • 接收客户端连接(udp通信时,不需要用到此函数)
        至此就可以和客户端联通,进行读写操作了,需要注意的是其返回值,是一个新的socket,而后续读写是在这个新的socket下进行的而不是一开始创建的socket。
  • 客户端相关函数

    • 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_connectsocket_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:
    <?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多路复用,后续会总结。

关联

posted @ 2020-07-19 09:04  漫游云巅  阅读(1039)  评论(0编辑  收藏  举报