
正文
websocket服务器+客户端
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
<?php
$demo = new ws('192.168.90.47',12345);
$demo->run(); class ws
{
//当前服务端主连接
private $currentFirstSocket;
//存放客户端socket连接
private $socketList = array();
//存放自定义的客户端连接
private $clientList = array(); public function __construct($address, $port)
{
$this->currentFirstSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($this->currentFirstSocket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($this->currentFirstSocket, $address, $port);
socket_listen($this->currentFirstSocket,2); $this->socketList[] = $this->currentFirstSocket;
self::notice('Server Started ...');
self::notice('Listening on : ' . $address . ' port ' . $port);
} public function run()
{
while (TRUE) {
$connect = $this->socketList;
$write = NULL;
$except = NULL;
self::notice(count($this->socketList) . " wait to connect ....");
socket_select($connect, $write, $except, NULL); foreach ($connect as $currentConnect) {
self::notice($currentConnect . " into connect"); //第一次连接
if ($currentConnect === $this->currentFirstSocket) {
$connectSuccessHandle = socket_accept($this->currentFirstSocket);
$this->socketList[] = $connectSuccessHandle;
$this->clientList[] = array(
"handshake" => false,
"clientHandle" => $connectSuccessHandle
);
self::notice($connectSuccessHandle. " new connect success ...");
} else {
$currentConnectKey = $this->searchSocketByHandle($currentConnect);
//第二次接收到的是客户端发送的websocket头协议
$len = @socket_recv($currentConnect, $buffer, 2048, 0);
self::notice("len ".$len);
if ($len < 10) { //如果接收到的信息长度小于10当作客户端退出
$this->socketExit($currentConnectKey);
self::notice($currentConnect . " exit" . " have:" . count($this->socketList));
break;
}
//检查是否握手,去握手
if ($this->clientList[$currentConnectKey]['handshake'] === false) {
self::notice($currentConnect." handshake success...");
$this->handshake($currentConnectKey, $buffer);
} else {
//正式连接,解包客户端的信息
$buffer = $this->unmask($buffer);
self::notice($currentConnect." send to server..."); $this->send($buffer);
}
}
}
}
} /*
* 握手
* */
private function handshake($currentConnectKey, $buffer)
{
$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));
$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->clientList[$currentConnectKey]['clientHandle'], $new_message, strlen($new_message));
$this->clientList[$currentConnectKey]['handshake'] = true;
} /*
* 客户端退出处理
* */
private function socketExit($currentConnectKey)
{
$currentSocket = $this->clientList[$currentConnectKey]['clientHandle'];
socket_shutdown($currentSocket);
socket_close($currentSocket);
foreach ($this->socketList as $key => $val) {
if ($val === $currentSocket) {
self::notice("delete " . $this->socketList[$key]);
unset($this->socketList[$key]);
}
}
unset($this->clientList[$currentConnectKey]);
} /*
* 发送信息给所有客户端
* */
private function send($sendMsg)
{
$sendMsg = $this->mask($sendMsg);
foreach ($this->clientList as $val) {
self::notice("write to " . $val['clientHandle'] . " " . $sendMsg);
socket_write($val['clientHandle'], $sendMsg, strlen($sendMsg));
}
} /*
* 解包客户端发送的信息
* */
function unmask($text)
{
$length = ord($text[1]) & 127;
if ($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
} elseif ($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
} else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
/*
* 封包信息发送给客户端
* */
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text); if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif ($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif ($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header . $text;
} private function searchSocketByHandle($handle)
{
foreach ($this->clientList as $key => $value) {
if ($value['clientHandle'] === $handle) {
return $key;
}
}
return false;
} private static function notice($message, $num = 1)
{
echo date("H:i:s") . " " . $message;
echo str_repeat("\n", $num);
}
}
客户端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>client</title>
<script src="bootstrap/js/jq.js"></script>
<script>
var socket;
$(document).ready(function(e) {
connect();
}); function connect(){
var url='ws://192.168.174.132:12345';
socket = new WebSocket(url);
socket.onopen=function(){
if(socket.readyState==1){
socket.send("hello this is html");
}else{
console.log("login fail");
}
}
socket.onmessage=function(msg){
console.log(msg);
} socket.onclose=function(){
console.log("close");
}
} function sendMsg()
{
var contetent = $("#test").val();
console.log("send: "+contetent);
socket.send(contetent);
} </script>
</head>
<body><input type="input" id="test">
<input type="button" onclick="sendMsg()" value="send"></body>
</html>






