PHP使用socket实现聊天通讯

代码进行简单配置,能直接跑起来。直接上代码

class ws_socket{

  public $socket = null;     //服务端创建的socket

  public $sockts = [];        //连接池

  public $user= [];            //保存所有链接的用户

  public $write = null;       //socket_select第二个参数

  public $except = null;    //socket_select第三个参数  

  public function __construct($ip,$port){

    $this->socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);    //创建

    socket_set_option($this->socket,SOL_SOCKET,SO_REUSEADDR,true);    //设置

    socket_bind($this->socket,$ip,$port);                   //绑定

    socket_listen($this->socket);                         //监听

  }

  //用户连接开始聊天
  public function run(){
    $this->sockts[] = $this->socket;
    while(true){
      $tmp_sockets = $this->sockts;
      socket_select($tmp_sockets,$write,$except,null);   //关键函数,IO复用,多用户链接(1,2,3参数都是引用传值)

      foreach($tmp_sockets as $sock){
        if($sock==$this->socket){
          $conSock = socket_accept($this->socket);    //新用户,保存到连接池
          $this->sockts[] = $conSock;
          $this->user[] = ['socket'=>$conSock,'handshake'=>false];
        }else{
          
          $request = socket_read($sock,8024);       //接收用户消息
          $k = $this->getUserIndex($sock);       //找到当前发送用的在连接池的$k
          if(strlen($request)==8){            //用户退出 关闭该连接池
            $this->close($k);           //关闭当前关闭的链接
            continue;
          }
          //判断是否有握手,没有握手先握手,有握手直接发生消息
          if(!$this->user[$k]['handshake']){
            $response = $this->handleShake($request);
            socket_write($sock,$response,strlen($response));
            $this->user[$k]['handshake'] = true;
          }else{
            $msg = $this->decode($request);
            $jsonArr = json_decode($msg);
            $this->send($jsonArr,$k);
          }
        }
      }
    }
  }

  //关闭链接
  private function close($k){
    //找到关闭的链接 并在连接池删除
    socket_close($this->user[$k]['socket']);
    unset($this->user[$k]);
    //将连接池清空,再将user重新赋值给连接池
    $this->sockts = null;
    $this->sockts[] = $this->socket;
    foreach($this->user as $v){
      $this->sockts[] = $v['socket'];
    }
  }

  //获取用户的$k
  private function getUserIndex($sock){
    foreach($this->user as $k=>$v){
      if($v['socket']==$sock){
        return $k;
      }
    }
  }

  //编码函数
  private function encode($msg) {
    $frame = [];
    $frame[0] = '81';
    $len = strlen($msg);
    if ($len < 126) {
      $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
    } else if ($len < 65025) {
      $s = dechex($len);
      $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
    } else {
      $s = dechex($len);
      $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
    }
    $data = '';
    $l = strlen($msg);
    for ($i = 0; $i < $l; $i++) {
      $data .= dechex(ord($msg{$i}));
    }
    $frame[2] = $data;
    $data = implode('', $frame);
    return pack("H*", $data);
  }

  //编码函数
  private function decode($buffer) {
    $decoded = '';
    if ( !empty($buffer[1]) ) {
      $len = ord($buffer[1]) & 127;
    }else{
      $len = '';
    }
    if ($len === 126) {
      $masks = substr($buffer, 4, 4);
      $data = substr($buffer, 8);
    } else if ($len === 127) {
      $masks = substr($buffer, 10, 4);
      $data = substr($buffer, 14);
    } else {
      $masks = substr($buffer, 2, 4);
      $data = substr($buffer, 6);
    }
    for ($index = 0; $index < strlen($data); $index++) {
      $decoded .= $data[$index] ^ $masks[$index % 4];
    }
    return $decoded;
  }

  //握手函数
  private function handleShake($request){
    $buf = substr($request,strpos($request,'Sec-WebSocket-Key:')+18);
    $key = trim(substr($buf,0,strpos($buf,"\r\n")));
    $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
    $new_message = "HTTP/1.1 101 Switching Protocols\r\n";
    $new_message .= "Upgrade: websocket\r\n";
    $new_message .= "Sec-WebSocket-Version: 13\r\n";
    $new_message .= "Connection: Upgrade\r\n";
    $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
    return $new_message;
  }

  //发送消息,业务处理  $msg参数是客服端发送过来的信息
  private function send($msg,$k){

    //需要发送的数据

    $res['replyname'] = '小白';
    $res['replyid'] = 1;
    $res['msgtype'] = 2;
    $res['isline'] = '在线';
    $res = $this->encode(json_encode($res));

    socket_write($this->user[$k]['socket'],$res,strlen($res));    //发送

  }

}

$obj = new Ws(0,8888);
$obj->run();

后面会把webSocket的代码也分享出来,有什么问题评论留言

posted @   Mr丿Luo  阅读(2120)  评论(0编辑  收藏  举报
编辑推荐:
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
阅读排行:
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
点击右上角即可分享
微信分享提示