<?php
/**
* Created by PhpStorm.
* User: suzim
* Date: 2019/2/4
* Time: 6:49
*/
namespace socket;
$ip = '0.0.0.0';
$port = 8888;
class Chat{
protected $master = null; //服务端socket
protected $connectPool = []; //连接池
protected $handPool = []; //http升级webSocket池
public function __construct($ip,$port)
{
$this->startSocket($ip,$port);
}
public function startSocket($ip,$port)
{
//创建服务端socket
//参数:domain:可用的地址/协议系列 ,IPV4
//参数type:使用的通信类型 ,字节流
//参数protocol:参数设置domain在返回的套接字上进行通信时要使用的指定内的特定协议,TCP
$this->connectPool[] = $this->master = \socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
//设置属性,绑定端口,监听端口
\socket_bind($this->master,$ip,$port);
\socket_listen($this->master,100);
//开启阻塞模式
while (true){
$sockets = $this->connectPool;
$write = $except = null;
\socket_select($sockets,$write,$except,60);
//
foreach($sockets as $socket)
{
if($socket == $this->master)
{
$this->connectPool[] = $client = \socket_accept($this->master);
$keyArr = \array_keys($this->connectPool,$client);
$key = end($keyArr);
$this->handPool[$key] = false;
}else{
//接受客户端消息
$length = \socket_recv($socket,$buffer,1024,0);
//小于7为断开连接,在服务器端断开连接。
if($length < 7)
{
$this->close($socket);
}else{
//将连接的http升级为webSocket
$key = array_search($socket,$this->connectPool);
if($this->handPool[$key] == false)
{
//升级协议
$this->handShake($socket,$buffer,$key);
}else{
//解帧
$this->deFrame($buffer);
//封帧
$this->enFrame($buffer);
//发送群聊
}
}
}
}
}
}
//关闭连接
public function close($socket)
{
$key = array_search($socket,$this->connectPool);
unset($this->connectPool[$key]);
unset($this->handPool[$key]);
\socket_close($socket);
}
//http升级webSocket
public function handShake($socket,$buff,$key)
{
//提取websocket传的key并进行加密 (这是固定的握手机制获取Sec-WebSocket-Key:里面的key)
$buf = substr($buff,strpos($buff,'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));
$requestHeader = "HTTP/1.1 101 Switching Protocols\r\n";
$requestHeader .= "Upgrade: websocket\r\n";
$requestHeader .= "Sec-WebSocket-Version: 13\r\n";
$requestHeader .= "Connection: Upgrade\r\n";
$requestHeader .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
//将套接字写入缓冲区,升级协议成功
socket_write($socket,$requestHeader,strlen($requestHeader));
//标记此套接字握手成功
$this->handPool[$key] = true;
}
//解帧
public function deFrame($buff)
{
//$buff 解析数据帧
$mask = array();
$data = '';
$msg = unpack('H*', $buff); //用unpack函数从二进制将数据解码
$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));
//遇到的问题 刚连接的时候就发送数据 显示 state connecting
$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++;
}
//发送数据到客户端
//如果长度大于125 将数据分块
$block = str_split($data, 125);
$mess = array(
'mess' => $block[0],
);
return $mess;
}
}
public function enFrame($buff)
{
$len = strlen($buff);
if($len <= 125)
{
return "\81".chr($len).$buff;
}elseif($len <= 65535)
{
return "\81".chr(126).pack("n",$len).$buff;
}else{
return "\81".chr(127).pack("xxxxN",$len).$buff;
}
}
//发送群聊
public function send($message)
{
foreach($this->connectPool as $socket)
{
if($socket != $this->master)
{
\socket_write($socket,$message,strlen($message));
}
}
}
}
php基于socket开发简单页面聊天
最新推荐文章于 2023-02-09 15:46:20 发布