一、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连接。