Nodejs 新手关于使用 socket.io 建立一个简单聊天室的问题

Nodejs 新手关于使用 socket.io 建立一个简单聊天室的问题

各位大神,我是一个node.js新手,最近在学习《《Node.js实战》》这本书进行学习,书上有一个简单聊天室的例子,主要用socket.io 来实现。下面是我的仓库,我写了一半的项目名字是:node_chat,代码不多,都在里边, 我的仓库 下图为服务器文件的一段代码 服务器文件.png 有用户链接上来后,执行guestNumber,joinRoom,handleNameChangeAttempts等方法。 assignGuestName.png 以上图assignGuestName为例子,执行完毕后用 socket.emit(‘nameResult’,{ success:true, name:name }); 给客户端返回,客户端成功用socket.on()接收。 同样, joinRoom.png JoinRoom函数调用后客户段也成功接收,下面上一些客户端的代码 客户端.png 但是handleNameChangeAttempts却出问题了,下面上其代码 handleNameChangeAttempts.png 本人调试可知已经执行到了 socket.emit(‘nameResult’,{ success:true, name:name }); socket.broadcast.to(currentRoom[socket.id]).emit(‘message’,{ text:previousName + 'is now known as ’ + name + ‘.’ }); 可是执行完之后就返回了404,跳不到客户端的index.html了,奇怪为什么都执行到socket.emit了,前两个方法可以,后两个方法就错了,调试了挺久找不到问题,求助各位大神


4 回复

当然,我可以帮助你解决这个问题。根据你的描述,handleNameChangeAttempts 方法出现问题,导致无法正确发送消息到客户端。让我们一起检查并修正这个问题。

服务器端代码

首先,我们来看一下 handleNameChangeAttempts 方法。假设你的代码类似于以下结构:

function handleNameChangeAttempts(socket) {
    socket.on('attemptNameChange', function(name) {
        var currentName = getName(socket);
        if (name !== currentName) {
            if (nameExists(name)) {
                socket.emit('nameResult', {
                    success: false,
                    message: 'That name is already taken.'
                });
            } else {
                socket.emit('nameResult', {
                    success: true,
                    name: name
                });

                // 广播名字变更信息
                socket.broadcast.to(currentRoom[socket.id]).emit('message', {
                    text: currentName + ' is now known as ' + name + '.'
                });

                // 更新用户名
                changeUserName(socket, name);
            }
        }
    });
}

客户端代码

确保客户端代码正确处理这些事件。例如:

<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();

// 处理名字变更结果
socket.on('nameResult', function(result) {
    if (result.success) {
        console.log('You are now known as ' + result.name + '.');
    } else {
        console.log(result.message);
    }
});

// 处理消息广播
socket.on('message', function(message) {
    console.log(message.text);
});
</script>

可能的问题及解决方案

  1. 检查房间分配:确保 currentRoom 对象中包含正确的房间信息。你可以打印 currentRoom 来检查是否有错误。

    console.log(currentRoom);
    
  2. 检查 socket.id:确保 socket.id 正确地映射到房间。你可以手动检查或调试。

  3. 确保正确的事件监听器:确保客户端正确地监听了 nameResultmessage 事件。

  4. 检查网络请求:如果客户端返回404错误,可能是某些静态资源(如HTML文件)没有正确加载。确保你的服务器正确地提供了这些资源。

示例代码

以下是完整的服务器端代码示例:

const io = require('socket.io')(server);

let currentRoom = {};
let usernames = {};

io.on('connection', function(socket) {
    socket.on('addUser', function(username) {
        // 添加用户名到全局变量
        usernames[username] = username;
        socket.username = username;
        socket.room = 'default';
        socket.join('default');
        currentRoom[socket.id] = 'default';

        // 发送欢迎消息
        socket.emit('updatechat', 'SERVER', 'you have connected to default');
        socket.broadcast.to('default').emit('updatechat', 'SERVER', username + ' has connected');
    });

    socket.on('switchRoom', function(newRoom) {
        socket.leave(socket.room);
        socket.join(newRoom);
        socket.room = newRoom;
        currentRoom[socket.id] = newRoom;

        socket.emit('updatechat', 'SERVER', 'you have connected to ' + newRoom);
        socket.broadcast.to(socket.room).emit('updatechat', 'SERVER', socket.username + ' has connected to ' + newRoom);
    });

    socket.on('sendMessage', function(msg) {
        socket.broadcast.to(socket.room).emit('updatechat', socket.username, msg);
    });

    handleNameChangeAttempts(socket);
});

function handleNameChangeAttempts(socket) {
    socket.on('attemptNameChange', function(name) {
        var currentName = socket.username;
        if (name !== currentName) {
            if (usernames[name]) {
                socket.emit('nameResult', {
                    success: false,
                    message: 'That name is already taken.'
                });
            } else {
                socket.emit('nameResult', {
                    success: true,
                    name: name
                });

                socket.broadcast.to(currentRoom[socket.id]).emit('message', {
                    text: currentName + ' is now known as ' + name + '.'
                });

                socket.username = name;
                usernames[name] = name;
            }
        }
    });
}

希望这些建议能帮助你解决问题!


看了看你的代码,我觉得你这里出现的问题应该是没有理解 nodejs 的异步到底是怎么回事。

我看代码中有不少 emit 和 broadcast 的操作都没有接收回调函数,你是按同步代码的写法在写 nodejs 的异步代码,这样是很容易出错的。

你调调看是不是这方面的原因

呃,有一处 $(’$send-message’) 应该是 $(’#send-message’)。 哎,用node-monkey也很难调试找到这种bug啊。

根据你的描述,handleNameChangeAttempts 方法在执行 socket.emitsocket.broadcast.to 时遇到了问题,导致客户端返回 404 错误。这可能是由于以下几个原因:

  1. 房间 ID 或 Socket ID 错误:确保你在 socket.broadcast.to(currentRoom[socket.id]) 中使用的房间 ID 是正确的。
  2. Socket.io 版本问题:不同版本的 Socket.io API 可能有所不同,确保你使用的 API 与 Socket.io 版本匹配。
  3. 事件名称或参数错误:确保你在客户端监听的事件名称和参数与服务端发送的完全一致。

以下是一个简单的示例,展示如何正确处理用户名更改:

服务器端代码示例

const io = require('socket.io')(server);

io.on('connection', (socket) => {
    let currentRoom;

    const assignGuestName = (socket, name, rooms) => {
        // 赋予一个默认用户名并加入房间
        socket.emit('nameResult', {
            success: true,
            name: name
        });
    };

    const joinRoom = (socket, room) => {
        socket.join(room);
        currentRoom = room;
        socket.emit('roomJoined', { room: room });
        socket.broadcast.to(room).emit('message', { text: `${socket.client.id} has joined ${room}.` });
    };

    const handleNameChangeAttempts = (socket, name, previousName, rooms) => {
        const isTaken = rooms.some(room => room.name === name);
        if (isTaken) {
            socket.emit('nameResult', {
                success: false,
                message: 'The desired name is already taken.'
            });
        } else {
            socket.emit('nameResult', {
                success: true,
                name: name
            });

            socket.broadcast.to(currentRoom).emit('message', {
                text: `${previousName} is now known as ${name}.`
            });
        }
    };

    socket.on('createRoom', (room) => {
        joinRoom(socket, room);
    });

    socket.on('setName', (data) => {
        handleNameChangeAttempts(socket, data.name, data.previousName, []);
    });
});

客户端代码示例

<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io();

    function createRoom(room) {
        socket.emit('createRoom', room);
    }

    function setName(name) {
        socket.emit('setName', { name: name, previousName: document.getElementById('currentName').innerText });
    }

    socket.on('nameResult', (result) => {
        if (result.success) {
            document.getElementById('currentName').innerText = result.name;
        } else {
            alert(result.message);
        }
    });

    socket.on('message', (msg) => {
        console.log(msg.text);
    });
</script>

关键点解释

  1. assignGuestName: 发送用户名结果给客户端。
  2. joinRoom: 加入房间,并广播消息给其他用户。
  3. handleNameChangeAttempts: 检查用户名是否已存在,如果不存在则更新用户名,并广播给其他用户。

请确保你的代码结构和逻辑与上述示例一致,并检查网络请求以确保没有 404 错误。

回到顶部