目前的代码只适合一个人用,逻辑中排斥多用户
1、服务端
/**
* php /data/www/g-mall/test.gsj.com/websocket/socket_server.php
* 浏览器打开 http://test.gsj.com/websocket/socket_client.html
* Class SocketService
*/
class SocketService
{
private $address;//ip地址
private $port;//端口
private $_sockets; //对象
public function __construct($address = '', $port = '')
{
if (!empty($address)) {
$this->address = $address;
}
if (!empty($port)) {
$this->port = $port;
}
}
//创建对象
public function service()
{
//获取tcp协议号码。
$tcp = getprotobyname("tcp");
//创建协议
$sock = socket_create(AF_INET, SOCK_STREAM, $tcp);
//socket_set_option ( resource $socket , int $level , int $optname , mixed $optval )用法说明:
//函数将 optname 参数在指定的 protocollevel 上指定的选项设置为该套接字的 optval 参数所指向的值
socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
if ($sock < 0) {
throw new Exception("failed to create socket: " . socket_strerror($sock) . "\n");
}
//绑定端口和ip
socket_bind($sock, $this->address, $this->port);
//在具体的地址下监听 socket 资源的收发操作
socket_listen($sock, $this->port);
// echo "listen on $this->address $this->port ... \n";
$this->_sockets &#61; $sock;
}
//运行
public function run()
{
//创建
$this->service();
//获取客户端
$clients[] &#61; $this->_sockets;
while (true) {
$changes &#61; $clients;
$write &#61; NULL;
$except &#61; NULL;
//在指定的超时时间内对给定的套接字数组运行select&#xff08;&#xff09;系统调用
socket_select($changes, $write, $except, NULL);
//
foreach ($changes as $key &#61;> $_sock) {
echo $_sock;
if ($this->_sockets &#61;&#61; $_sock) { //判断是不是新接入的socket
//socket_accept 接收一个新的 socket 资源 函数原型
if (($newClient &#61; socket_accept($_sock)) &#61;&#61;&#61; false) {
die(&#39;failed to accept socket: &#39; . socket_strerror($_sock) . "\n");
}
//socket_read 获取传送的数据 函数原型$newClient 1024长度
$line &#61; trim(socket_read($newClient, 1024));
//握手处理
$this->handshaking($newClient, $line);
//获取client ip 查询给定套接字的远程端&#xff0c;可能返回主机端口或者Unix文件系统路径&#xff08;取决于socket_create设置的类型&#xff09;
socket_getpeername($newClient, $ip);
$clients[$ip] &#61; $newClient;
} else {
//函数 socket_recv() 从 socket 中接收长度为 length 字节的数据&#xff0c;并保存在 data 中
socket_recv($_sock, $buffer, 2048, 0);
//解析接收数据
$msg &#61; $this->message($buffer);
//在这里业务代码
echo "{$key} clinet msg:", $msg, "\n";
// fwrite(STDOUT, &#39;Please input a argument:&#39;);
// $response &#61; trim(fgets(STDIN));
$response&#61;"接受了";
//发送
$this->send($_sock, $response);
echo "{$key} response to Client:" . $response, "\n";
}
}
}
}
/**
* 握手处理
* &#64;param $newClient socket
* &#64;return int 接收到的信息
*/
public function handshaking($newClient, $line)
{
$headers &#61; array();
//$line消息
$lines &#61; preg_split("/\r\n/", $line);
foreach ($lines as $line) {
$line &#61; chop($line);
if (preg_match(&#39;/\A(\S&#43;): (.*)\z/&#39;, $line, $matches)) {
$headers[$matches[1]] &#61; $matches[2];
}
}
//首先&#xff0c; Sec-WebSocket-Key
//是一个 Base64 的值&#xff0c;这个是浏览器随机生成的&#xff0c;告诉服务器&#xff1a; &#xff0c;我要验证尼是不是真的是Websocket助理
$secKey &#61; $headers[&#39;Sec-WebSocket-Key&#39;];
$secAccept &#61; base64_encode(pack(&#39;H*&#39;, sha1($secKey . &#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;)));
$upgrade &#61; "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $this->address\r\n" .
"WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n" .
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
//返回
return socket_write($newClient, $upgrade, strlen($upgrade));
}
/**
* 解析接收数据
* &#64;param $buffer
* &#64;return null|string
*/
public function message($buffer)
{
$len &#61; $masks &#61; $data &#61; $decoded &#61; null;
$len &#61; ord($buffer[1]) & 127;
if ($len &#61;&#61;&#61; 126) {
$masks &#61; substr($buffer, 4, 4);
$data &#61; substr($buffer, 8);
} else if ($len &#61;&#61;&#61; 127) {
$masks &#61; substr($buffer, 10, 4);
$data &#61; substr($buffer, 14);
} else {
$masks &#61; substr($buffer, 2, 4);
$data &#61; substr($buffer, 6);
}
for ($index &#61; 0; $index < strlen($data); $index&#43;&#43;) {
$decoded .&#61; $data[$index] ^ $masks[$index % 4];
}
return $decoded;
}
/**
* 发送数据
* &#64;param $newClinet 新接入的socket
* &#64;param $msg 要发送的数据
* &#64;return int|string
*/
public function send($newClinet, $msg)
{
$msg &#61; $this->frame($msg);
socket_write($newClinet, $msg, strlen($msg));
}
//转义处理
public function frame($s)
{
$a &#61; str_split($s, 125);
if (count($a) &#61;&#61; 1) {
return "\x81" . chr(strlen($a[0])) . $a[0];
}
$ns &#61; "";
foreach ($a as $o) {
$ns .&#61; "\x81" . chr(strlen($o)) . $o;
}
return $ns;
}
/**
* 关闭socket
*/
public function close()
{
return socket_close($this->_sockets);
}
}
$sock &#61; new SocketService(&#39;192.168.31.20&#39;, &#39;9000&#39;);
$sock->run();
命令行运行服务端&#xff1b;
2、客户端
close Connect to Service response:"doctype html>
<html lang&#61;"en">
<head>
<meta charset&#61;"UTF-8">
<meta name&#61;"viewport" content&#61;"width&#61;device-width,initial-scale&#61;1, maximum-scale&#61;1, user-scalable&#61;no">
<title>websockettitle>
head>
<body>
<input id&#61;"text" value&#61;"">
<input type&#61;"submit" value&#61;"send" onclick&#61;"start()">
<input type&#61;"submit" value&#61;"close" onclick&#61;"close()">
<div id&#61;"msg">div>
<script>
/**
*0&#xff1a;未连接
*1&#xff1a;连接成功&#xff0c;可通讯
*2&#xff1a;正在关闭
*3&#xff1a;连接已关闭或无法打开
*/
//创建一个webSocket 实例
var webSocket &#61; new WebSocket("ws://192.168.31.20:9000");
webSocket.onerror &#61; function (event) {
onError(event);
};
// 打开websocket
webSocket.onopen &#61; function (event) {
onOpen(event);
};
//监听消息
webSocket.onmessage &#61; function (event) {
onMessage(event);
};
webSocket.onclose &#61; function (event) {
onClose(event);
}
//关闭监听websocket
function onError(event) {
document.getElementById("msg").innerHTML &#61; "
console.log("error" &#43; event.data);
};
function onOpen(event) {
console.log("open:" &#43; sockState());
document.getElementById("msg").innerHTML &#61; "
};
function onMessage(event) {
console.log("onMessage");
document.getElementById("msg").innerHTML &#43;&#61; "
close
";request"
&#43; msg &#43; ""