zoukankan      html  css  js  c++  java
  • 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
      ";
        //发送给客户端,注意此时用的是$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多路复用,后续会总结。

    关联

  • 相关阅读:
    消息队列设计
    抓包工具Fiddler
    分布式系统和CAP
    Topshelf组件
    Parallel.For
    MVC插件
    Azure Messaging-ServiceBus Messaging
    MVC
    requireJS
    第一次react-native项目实践要点总结 good
  • 原文地址:https://www.cnblogs.com/vishun/p/10469773.html
Copyright © 2011-2022 走看看