PHP原生Socket.io实现TIM即时通讯的坑
2年前
阅读 6473
评论 0
喜欢 0
## 1、长连接
原生PHP-Socket.io的长连接主要是使用`while(true)`+`服务器端堵塞`的方式实现的,这种长连接实现方式及其耗费性能,所以折中选择了一个优化的方案,鉴于博客没多少流量,我们在`whlie(true)`中使用了`sleep(1)`的方法,让循环1秒执行一次,这样降低了长连接的执行效率,释放了一部分内存,不过`WebSocket`会出现偶尔丢包的概率。
## 2、server服务端解包与打包代码
```php
/**
* 解码客户端发送过来的信息
* @param binary $buffer 客户端传来的信息
* @return String $decoded 解码后的字符串
*/
private function decodeMsg($buffer) {
$len = $masks = $data = $decoded = null;
$len = ord($buffer[1]) & 127;
if ($len === 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} else if ($len === 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
$len = strlen($data);
for ($index = 0; $index < $len; $index++) {
$decoded .= $data[$index] ^ $masks[$index % 4];
}
return $decoded;
}
/**
* 发送到客户端前进行编码
* @param string $msg 发送到客户端的内容
*/
private function encodeMsg($msg) {
$len = strlen($msg);
if ($len <= 125) {
return "\x81" . chr($len) . $msg;
} else if ($len <= 65535) {
return "\x81" . chr(126) .pack("n", $len). $msg;
} else {
return "\x81" . chr(127) .pack("xxxxN", $len). $msg;
}
}
```
## 3、WebSocket心跳重连
WebSocket的API有个特点,是它有个超时机制,好像是1分钟内无任何数据传输操作,就主动发送关闭请求,这时候我们就需要自己用代码实现一个心跳操作,检测连接是否已经关闭,关闭的话则重新打开。
```javascript
var lockReconnect = false;//避免重复连接
var wsUrl = '你的server地址';
var ws;
var tt;
createWebSocket();
// ①开启WebSocket
function createWebSocket() {
try {
ws = new WebSocket(wsUrl);
init();
} catch(e) {
reconnect(wsUrl);
}
}
// ②初始化WebSocket,并设置心跳检测
function init() {
// 接收Socket断开时的消息通知
ws.onclose = function(){
console.log("断开socket连接了。。。试图重新连接。。。。");
reconnect(wsUrl);
};
// 接收Socket连接失败时的异常通知
ws.onerror = function(e){
console.log("ERROR:" + e.data);
reconnect(wsUrl);
};
// 连接成功
ws.onopen = function () {
console.log("握手成功,打开socket连接了。。。");
//心跳检测重置
heartCheck.start();
};
var message = '';
var flag = true;
// 接收服务端广播的消息通知
ws.onmessage = function(e){
heartCheck.start();
if (e.data == '123456789') { return false;}
message += e.data;
try {
obj = JSON.parse(e.data);
} catch(error_1) {
try {
obj = JSON.parse(message);
flag = true;
} catch (error_2) {
flag = false;
}
}
if (flag !== false) {
console.log(message);
message = '';
}
};
}
// ③ 掉线重连
function reconnect(url) {
if(lockReconnect) {
return;
};
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
tt && clearTimeout(tt);
tt = setTimeout(function () {
createWebSocket(url);
lockReconnect = false;
}, 4000);
}
// ④心跳检测
var heartCheck = {
timeout: 3000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
var self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
ws.send("123456789");
//self.serverTimeoutObj = setTimeout(function() {
//ws.close();
// createWebSocket();
//}, self.timeout);
}, this.timeout)
}
}
```