websocket怎么实现单聊功能?

如题,怎么实现单聊功能并给指定连接设备发送消息?

阅读 2.9k
3 个回答

前端发送消息时指定 接收方用户ID,服务端收到消息后将消息持久化(存储至数据库等),若接收方用户 在线 则将消息发送即可,以下为 nodejs 实现的简单示例:

const WebSocket = require("ws");

const server = new WebSocket.Server({ port: 8080 });

const clients = new Map();

server.on("connection", (socket) => {
  socket.on("message", (msg) => {
    const message = JSON.parse(msg);

    switch (message.type) {
      case "online": {
        clients.set(message.userId, socket);
        console.log(`用户(${message.userId})已上线`);
        break;
      }
      case "message": {
        const record = {
          type: "message",
          fromUserId: message.fromUserId,
          toUserId: message.toUserId,
          text: message.text
        };

        // 存储消息至数据库 collection.insertOne(record);

        const target = clients.get(message.toUserId);
        if (target != null) {
          // 若目标用户在线,则发送消息至目标用户
          target.send(JSON.stringify(record));
        }

        break;
      }
    }
  });

  socket.on("close", () => {
    for (const [key, value] of clients.entries()) {
      if (value === socket) {
        clients.delete(key);
        console.log(`用户(${key})已下线`);
        break;
      }
    }
  });
});

给当前的 websocket 会话记录一个唯一的 id,本端发 websocket 消息的时候指定对端会话的 id 就行了,用服务端来做转发

单聊可以在 socket 过程中加个身份认证,我这里用随机 id ,正常可以直接用用户名或者 uid ,只要能保证唯一就行

当然正常使用的 id 肯定是 持久化 的,不会像示例代码中只是用变量缓存,除非是临时的一次性聊天需求

服务端:

const WebSocketServer = require('websocket').server;
const http = require('http');

// 创建HTTP服务器
const server = http.createServer((req, res) => {});

// 创建WebSocket服务器
const wsServer = new WebSocketServer({
    httpServer: server
});

// 存储连接的客户端(用 set 也行)
const clients = {};

// 处理客户端连接请求
wsServer.on('request', (req) => {
    const connection = req.accept(null, req.origin);
    
    // 生成客户端ID(最好保证唯一性,此处教程简单演示)
    const clientId = Math.random().toString(36).substr(2, 9);
    
    // 存储客户端连接
    clients[clientId] = connection;
    
    // 发送连接成功消息给客户端
    connection.sendUTF(JSON.stringify({ type: 'connect', clientId }));
    
    // 处理收到的消息
    connection.on('message', (msg) => {
        if (msg.type === 'utf8') {
            const data = JSON.parse(msg.utf8Data);
            
            if (data.type === 'private_message') {
                const { to, message } = data;
                
                // 查找要发送给的客户端
                const targetClient = clients[to];
                
                if (targetClient) {
                    // 发送私聊消息给目标客户端
                    targetClient.sendUTF(JSON.stringify({ type: 'private_message', from: clientId, message }));
                } else {
                    // 目标客户端不存在,发送错误消息给发送方
                    connection.sendUTF(JSON.stringify({ type: 'error', message: '目标用户不存在' }));
                }
            }
        }
    });
    
    // 处理客户端关闭连接
    connection.on('close', () => {
        // 删除已关闭的连接
        delete clients[clientId];
    });
});

// 启动服务器
server.listen(8080, () => {
    console.log('WebSocket服务器已启动');
});

客户端1:

<!DOCTYPE html>
<html>
    <head>
        <title>客户端1</title>
    </head>
    <body>
        <h1>客户端1</h1>

        <div id="messages"></div>

        <form id="sendForm">
            <input type="text" id="toInput" placeholder="目标客户端ID">
            <input type="text" id="messageInput" placeholder="消息内容">
            <button type="submit">发送</button>
        </form>

        <script>
            const ws = new WebSocket('ws://localhost:8080');

            ws.onopen = () => {
                console.log('已连接到服务器');
            };

            ws.onmessage = (event) => {
                const data = JSON.parse(event.data);

                if (data.type === 'connect') {
                    console.log('连接成功,我的ID是:', data.clientId);
                } else if (data.type === 'private_message') {
                    console.log('收到私聊消息:', data.from, '发来的', data.message);
                    displayMessage(data.from, data.message);
                }
            };

            document.getElementById('sendForm').addEventListener('submit', (e) => {
                e.preventDefault();

                const to = document.getElementById('toInput').value;
                const message = document.getElementById('messageInput').value;

                const data = {
                    type: 'private_message',
                    to,
                    message
                };

                ws.send(JSON.stringify(data));

                document.getElementById('toInput').value = '';
                document.getElementById('messageInput').value = '';
            });

            function displayMessage(from, message) {
                const div = document.createElement('div');
                div.textContent = `${from}: ${message}`;
                document.getElementById('messages').appendChild(div);
            }
        </script>
    </body>
</html>

客户端2:

<!DOCTYPE html>
<html>
    <head>
        <title>客户端2</title>
    </head>
    <body>
        <h1>客户端2</h1>

        <div id="messages"></div>

        <form id="sendForm">
            <input type="text" id="toInput" placeholder="目标客户端ID">
            <input type="text" id="messageInput" placeholder="消息内容">
            <button type="submit">发送</button>
        </form>

        <script>
            const ws = new WebSocket('ws://localhost:8080');

            ws.onopen = () => {
                console.log('已连接到服务器');
            };

            ws.onmessage = (event) => {
                const data = JSON.parse(event.data);

                if (data.type === 'connect') {
                    console.log('连接成功,我的ID是:', data.clientId);
                } else if (data.type === 'private_message') {
                    console.log('收到私聊消息:', data.from, '发来的', data.message);
                    displayMessage(data.from, data.message);
                }
            };

            document.getElementById('sendForm').addEventListener('submit', (e) => {
                e.preventDefault();

                const to = document.getElementById('toInput').value;
                const message = document.getElementById('messageInput').value;

                const data = {
                    type: 'private_message',
                    to,
                    message
                };

                ws.send(JSON.stringify(data));

                document.getElementById('toInput').value = '';
                document.getElementById('messageInput').value = '';
            });

            function displayMessage(from, message) {
                const div = document.createElement('div');
                div.textContent = `${from}: ${message}`;
                document.getElementById('messages').appendChild(div);
            }
        </script>
    </body>
</html>

演示效果

客户端1


客户端2


但是现在还有一个问题,就是不能看到自己发送的消息,这个可以通过每次发送消息也携带自己的 id 来实现(代码我就没改了,题主有需求可以自行更新)
实现看到自己发送的消息

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题