websocket前后端实现心跳
root@tr-desktop:/www/wwwroot/troa/webAPI/public/websocket# cat -n hearbeat.html
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no">
6 <title>websocket</title>
7 </head>
8 <body>
9 <input id="text" value="">
10 <input type="submit" value="send" onclick="start()">
11 <input type="submit" value="close" onclick="close()">
12 <div id="msg"></div>
13 <script>
14 function connect() {
15 const socket = new WebSocket('ws://10.1.8.56:9090/chat');
16
17 let heartbeatInterval;
18
19 socket.onopen = function() {
20 console.log('WebSocket connection established.');
21
22 // 定期发送心跳
23 heartbeatInterval = setInterval(() => {
24 if (socket.readyState === WebSocket.OPEN) {
25 socket.send('ping'); // 发送心跳
26 }
27 }, 3000*20*2); // 每3秒发送一次心跳
28 };
29
30 socket.onmessage = function(event) {
31 console.log('Message from server:', event.data);
32
33 // 处理服务器的心跳响应
34 if (event.data === 'pong') {
35 console.log('Received pong from server');
36 }
37 };
38
39 socket.onclose = function(event) {
40 console.log('WebSocket connection closed:', event);
41 clearInterval(heartbeatInterval); // 清除心跳定时器
42
43 // 启动重连逻辑
44 setTimeout(() => {
45 connect();
46 // 重新连接的代码
47 }, 5000); // 5秒后重连
48 };
49 }
50 connect();
51
52 </script>
53 </body>
54 </html>
55
root@tr-desktop:/www/wwwroot/troa/webAPI/public/websocket# cat -n hearbeat.php
1 <?php
2 require 'vendor/autoload.php';
3
4 use Ratchet\Http\HttpServer;
5 use Ratchet\Server\IoServer;
6 use Ratchet\WebSocket\WsServer;
7 use Ratchet\MessageComponentInterface;
8 use Ratchet\ConnectionInterface;
9
10 class Chat implements MessageComponentInterface {
11 protected $clients;
12 protected $db; // 数据库连接
13
14
15 public function __construct() {
16 $this->clients = new \SplObjectStorage; // 存储所有连接的客户端
17 // 初始化数据库连接
18 $this->db = new PDO('mysql:host=localhost;dbname=troa_new', 'root', '123456');
19 $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
20 }
21
22 public function onOpen(ConnectionInterface $conn) {
23 // 新客户端连接
24 $this->clients->attach($conn);
25 echo "New connection: {$conn->resourceId}\n";
26 }
27
28 public function onMessage(ConnectionInterface $from, $msg) {
29 if ($msg === 'ping') {
30 $from->send('pong'); // 回复心跳
31 } else {
32 // 处理其他消息
33 }
34 if (strpos($msg, 'bind:') === 0) {
35 $userId = substr($msg, 5);
36 $this->bindClientToUser($from, $userId);
37 } else {
38 list($targetId, $message) = explode(':', $msg, 2);
39 $this->sendMessageToClient($targetId, $message);
40 }
41 }
42
43 public function onClose(ConnectionInterface $conn) {
44 // 客户端断开连接
45 $this->clients->detach($conn);
46 echo "Connection {$conn->resourceId} has disconnected\n";
47 }
48
49 public function onError(ConnectionInterface $conn, \Exception $e) {
50 echo "An error has occurred: {$e->getMessage()}\n";
51 $conn->close();
52 }
53 public function bindClientToUser(ConnectionInterface $conn, $userId) {
54 // 保存用户 ID 到数据库
55 $stmt = $this->db->prepare("INSERT INTO user_connections (user_id, connection_id) VALUES (:user_id, :connection_id)");
56 $stmt->execute(['user_id' => $userId, 'connection_id' => $conn->resourceId]);
57 echo "Client {$conn->resourceId} bound to user ID: {$userId}\n";
58 }
59 // 添加发送消息给指定客户端的方法
60 public function sendMessageToClient($clientId, $message) {
61 foreach ($this->clients as $client) {
62 if ('msg='.$client->resourceId === $clientId) {
63 $client->send($message);
64 break;
65 }
66 }
67 }
68 }
69 // 创建 WebSocket 服务器
70 $server = IoServer::factory(
71 new HttpServer(
72 new WsServer(
73 new Chat()
74 )
75 ),
76 9090 // 监听的端口
77 );
78 // 启动服务器
79 $server->run();