SOCKET流程:
1:服务器端开启端口等待客户端建立TCP连接
2:客户端建立TCP四次握手协议TCP连接 (TCP的四次握手连接全部被封装好的代码处理)
3: 客户端发起WS协议
4:服务器判断 客户端发送消息内容即是WS协议请求
5:服务器端返回WS连接协议
6:双方建立WS长连接
7:此时客户端可以接受服务器推送过来的内容(接受WS加密内容),也可以向服务器端发送内容(服务端解码WS加密内容)。
双方关闭SOCKET
先看下效果~~有待加强
(1)要先链接啊,链接成功后才可以发送消息
(2)下面就是代码的生成了
先要编写页面,先不写链接(界面中的各个按钮)
<textarea class="log" style="width: 100%; height: 500px;">
=======websocket======
</textarea>
<input type="button" value="连接" onClick="link()">
<input type="button" value="断开" onClick="dis()">
<input type="text" id="text">
<input type="button" value="发送" onClick="send()">
(3)编写链接功能
<?php
class WS {
//存储连接资源
private $clients;
//主线端口
private $master;
//客户资源
private $user;
private $ip;
private $port;
public function __construct($ip="127.0.0.1",$port = 3432) {
$this->ip = $ip;
$this->port = $port;
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$this->master = $this->WebSocket($this->ip,$this->port);
$this->clients = array('s'=>$this->master);
}
//建立服务器端连接
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);
$this->log('开始监听: '.$address.' : '.$port);
return $server;
}
function run() {
while (true) {
$read = $this->clients;
//var_dump($read);exit;
@socket_select($read, $write = NULL, $except = NULL, null);
/*if ( < 1) {
//如果没有连接跳出
continue;
}*/
foreach($read as $r) {
if($r == $this->master) {
//建立四步握手协议
//有客户端新进连接进来
//判断服务器socket是否被激活
//接受客户端,并将他添加到客户数组
$this->clients[] = $newCli = socket_accept($this->master);
$this->user[] = array(
'handle'=>false,
'socket'=>$newCli
);
$k = $this->search($newCli);
echo "NUM = ".$k."\n"; //服务器端输出当前连接的 用户ID,既是数组K
} else {
//已有的连接发来的消息所有的客户数据读取
// 读到换行符或1024字节
// 虽然客户端断开连接时显示错误,所以沉默错误消息
$buffer = '';
$data = socket_recv($r,$buffer ,2048,0);
// 检查客户端是否断开连接
if ($data < 7) {
// 删除客户端为客户数组
$k = $this->search($r);
echo "NO---".$k;
$this->close($r);
// continue to the next client to read from
continue;
}
$k = $this->search($r);
if($this->user[$k]['handle'] == false) { //只是进行了四步握手协议,此时浏览器发来WS协议请求
echo "$k=".$k."\n";
$this->handshake($k,$buffer); //返回WS协议应答
} else { //已经建立WS协议,获取通过WS发来的消息
$str = $this->uncode($buffer);
echo "\n".$str;
$this->sendAll($str,$r); //发送消息广播
}
}
}
}
}
//WebSocket 握手协议
function handshake($k,$buffer){
//获取KEY及生成新的KEY
$buf = substr($buffer,strpos($buffer,'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));
//返回HTTP协议
$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";
socket_write($this->user[$k]['socket'],$new_message,strlen($new_message));
$this->user[$k]['handle']=true;
return true;
}
//群发消息
function sendAll($str,$socket) {
// 把这所有的客户在客户数组(除了第一个,这是一个监听套接字)
foreach ($this->clients as $send_sock) {
// 如果这个资源等于 服务器或是等于它自己则跳过,
if ($send_sock == $this->master || $send_sock == $socket)
continue;
//发送给其他用户一个信息
$data = $this->code($str);
socket_write($send_sock, $data,strlen($data));
}
}
//查找用户
function search($sign) {//通过标示遍历获取id
foreach ($this->user as $k=>$v){
if($sign == $v['socket'])
return $k;
}
return false;
}
//断开连接
function close($sign){//通过标示断开连接
$k = array_search($sign, $this->clients);
socket_close($sign); //断开的连接需要在服务器端手动关闭
unset($this->clients[$k]);
unset($this->user[$k]);
}
//信息解码
function uncode($str){
$mask = array();
$data = '';
$msg = unpack('H*',$str);
$head = substr($msg[1],0,2);
if (hexdec($head{1}) === 8) {
$data = false;
}else if (hexdec($head{1}) === 1){
$mask[] = hexdec(substr($msg[1],4,2));
$mask[] = hexdec(substr($msg[1],6,2));
$mask[] = hexdec(substr($msg[1],8,2));
$mask[] = hexdec(substr($msg[1],10,2));
$s = 12;
$e = strlen($msg[1])-2;
$n = 0;
for ($i=$s; $i<= $e; $i+= 2) {
$data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2)));
$n++;
}
}
return $data;
}
//信息加码
function code($msg){
$msg = preg_replace(array('/\r$/','/\n$/','/\r\n$/',), '', $msg);
$frame = array();
$frame[0] = '81';
$len = strlen($msg);
$frame[1] = $len<16?'0'.dechex($len):dechex($len);
$frame[2] = $this->ord_hex($msg);
$data = implode('',$frame);
return pack("H*", $data);
}
function ord_hex($data) {
$msg = '';
$l = strlen($data);
for ($i= 0; $i<$l; $i++) {
$msg .= dechex(ord($data{$i}));
}
return $msg;
}
function log($t){//控制台输出
$t=$t."\r\n";
fwrite(STDOUT, iconv('utf-8','gbk//IGNORE',$t));
}
}
$ws = new WS("127.0.0.1",8000);
$ws->run();
(4)写js(就是单击链接按钮和断开按钮出现的字)记得要引入jq
function link(){
var url='ws://127.0.0.1:8000';
socket=new WebSocket(url);
socket.onopen=function(){
log('连接成功');
}
socket.onmessage=function(msg) {
log('获得消息:'+msg.data);
console.log(msg);
}
socket.onclose=function(){log('断开连接')}
}
function dis(){
socket.close();
socket=null;
}
function log(var1){
$('.log').append(var1+"\r\n");
}
function send(){
socket.send($('#text').val());
}
这样,简单的在线客服就完成了
在运行的时候要先运行ws,这样才能链接成功