通过实际案例,详细讲解在PHP中如何实现websocket编程

2023-03-07PHP语言

一、WebSocket协议

相对HTTP非持久化协议来说,WebSocket是一个持久化的协议。http的生命周期客户端和服务端是一个请求和一个返回回话就结束了,而WebSocket可以进行持久化连接,即客户端只需进行一次握手,成功后即可持续进行数据通信。WebSocket实现了client与server之间全双工通信,即server端有数据更新时可以主动推送给client端。WebSocket可以用在即时聊天、在线客服、在线会议等场景中。

二、客户端和服务端建立连接(握手)

1、客户端向服务端发送请求

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: mmt3.com

Upgrade:告诉服务器现在发送的是WebSocket协议

Sec-WebSocket-Key:一个Base64 encode的值,这个是浏览器随机生成的,用于验证服务器端返回数据

2、服务端处理并返回

服务端在获取客户端请求的信息后,根据WebSocket协议对数据进行处理并返回,其中要对Sec-WebSocket-Key进行加密等操作。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Upgrade:告诉客户端使用Websocket协议

Sec-WebSocket-Accept:注意跟客户端的不一样,这个是客户端带过来的Key加密过后的 Sec-WebSocket-Key。它是客户端要求建立WebSocket验证的凭证

三、服务器代码详细讲解

1、创建socket并监听端口

<?php
function WebSocket($address,$port){
  $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
  socket_bind($server, $address, $port);
  socket_listen($server);
  return $server;
}

传入相应的IP与端口来创建socket操作,socket_set_option中1表示接受所有的数据包。

2、设计一个循环挂起WebSocket通道,进行数据的接收、处理和发送

<?php
function run(){
 while(true){
  $changes=$this->sockets;
  $write=NULL;
  $except=NULL;
  socket_select($changes,$write,$except,NULL);
  foreach($changes as $sock){
   if($sock==$this->master){
    $client=socket_accept($this->master);
    $key=uniqid();
    $this->sockets[]=$client;
    $this->users[$key]=array(
     'socket'=>$client,
     'shou'=>false
    );
   }else{
    $len=0;
    $buffer='';
    do{
     $l=socket_recv($sock,$buf,1000,0);
     $len+=$l;
     $buffer.=$buf;
    }while($l==1000);
    $k=$this->search($sock);
    if($len<7){
     $this->send2($k);
     continue;
    }
    if(!$this->users[$k]['shou']){
     $this->woshou($k,$buffer);
    }else{
     $buffer = $this->uncode($buffer,$k);
     if($buffer==false){
      continue;
     }
     $this->send($k,$buffer);
    }
   }
  }
 }
}

1)该函数是对创建的socket循环进行监听,处理数据。这是一个死循环,直到socket断开。

2)socket_select:这个函数很重要,它是同时接受多个连接的关键,可以理解它是为了阻塞程序继续往下执行。

-参数$sockets:一个存放所有socket的数组,当它有变化时(有新消息到或者有客户端连接/断开),函数才会返回并继续往下执行。

-参数$write:监听是否有客户端写数据,传入NULL是不关心是否有写变化。

-参数$except:是$sockets里面要被排除的元素,传入NULL是”监听”全部。

-参数$n:是超时时间 (0:则立即结束 ;n>1: 则最多在n秒后结束;null:如遇某一个连接有新动态,则返回)

3)$sock==$this->master 表示有新的client连接进来。

4)socket_accept:接受到一个socket连接。

5)$key:给新连接进来的socket一个唯一的ID。

6)如何不是新连接,则执行下面两个操作1.为客户端断开socket连接,2.客户端发送信息

7)socket_recv($sock,$buf,1000,0);

该函数读取该socket的信息,其中$buf参数是引用传参即接收数据,第三个参数是接收数据的长度。

8)$len<7:如果接收的信息长度小于7,则该客户端的socket为断开连接。并在$this->sockets和$this->users里面进行删除。

9)如果没有握手,则进行握手操作。

10)最后是客户端发送的信息了,对接受到的信息进行uncode处理。如果不为空,则进行消息推送操作。

四、客户端代码讲解

var ws = new WebSocket("ws://IP:端口");
ws.onopen=function(){
 if(so.readyState==1){
  so.send('type=add&ming='+n);
 }
}
ws.onerror = function(){
 console.log("error");
};
ws.onmessage = function (msg){
 console.log(msg);
}
ws.onclose = function(){
 ws = false;
}

ws.onopen:握手监听函数,readyState状态为1证明握手成功,然后把向服务端发送数据。

ws.onerror:错误返回信息函数。

ws.onmessage:监听服务端推送的消息。

ws.onclose:断开WebSocket连接。

版权声明:本文为老张的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://www.webppp.com/view/php_socket_programming.html