You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lua TCP聊天室服务器无法处理多客户端连接问题求助

Lua TCP聊天室服务器:多客户端连接问题解决

问题描述

用Lua编写的TCP聊天室服务器支持单个客户端正常连接,但新客户端必须等待已有客户端退出才能成功接入。

问题原因

原代码中处理客户端消息的handle函数使用了阻塞式的client:receive(),再加上Lua协程是协作式调度的特性:当第一个客户端的协程进入receive()阻塞后,不会主动让出CPU,导致主循环的accept()逻辑虽设置了短超时,但协程无法切换回主循环处理新连接请求,最终服务器无法接受新客户端。

修复方案

使用LuaSocket的socket.select函数实现多路IO复用,同时监听服务器的连接请求和所有客户端的消息输入,避免阻塞并同时处理多个客户端。

修复后的server.lua

require('socket')

local server = assert(socket.bind('localhost', 69420))
server:settimeout(0) -- 设置服务器socket为非阻塞
local clients = {}

-- 广播消息给所有客户端
local function broadcast(message)
    if not message then return end
    for _, client in pairs(clients) do
        local ok, err = client:send(message .. '\n')
        -- 处理发送失败的客户端(已断开)
        if err then
            client:close()
            table.remove(clients, _)
        end
    end
end

-- 主循环:用select处理多路IO
local function mainLoop()
    print("localhost server listening on port 69420... nice")
    while true do
        -- 准备要监听的读socket:服务器socket + 所有客户端socket
        local readSockets = {server}
        for _, client in pairs(clients) do
            table.insert(readSockets, client)
        end

        -- 等待可读事件,无超时(也可设置超时比如1秒)
        local readable, _, err = socket.select(readSockets, nil)
        if err then
            print("select error:", err)
            break
        end

        -- 处理每个可读的socket
        for _, sock in pairs(readable) do
            -- 如果是服务器socket,说明有新连接
            if sock == server then
                local client = server:accept()
                if client then
                    client:settimeout(0) -- 设置客户端socket为非阻塞
                    table.insert(clients, client)
                    print("a new client entered")
                    broadcast("a new client has joined us")
                end
            else
                -- 如果是客户端socket,读取消息
                local buff, err = sock:receive()
                if err then
                    -- 客户端断开或出错
                    print("client error/disconnected:", err)
                    sock:close()
                    -- 从客户端列表移除
                    for i, c in pairs(clients) do
                        if c == sock then
                            table.remove(clients, i)
                            broadcast("a client has left")
                            break
                        end
                    end
                else
                    -- 广播收到的消息
                    print("received:", buff)
                    broadcast(buff)
                end
            end
        end
    end
end

mainLoop()

客户端代码无需修改(client.lua保持原样)

关键改动说明

  • 设置所有socket为非阻塞:服务器socket和客户端socket都调用settimeout(0),避免单个IO操作阻塞整个进程。
  • 使用socket.select管理多路IO:一次性监听所有需要处理的socket,当有socket可读时(新连接或客户端消息)再进行处理,避免无效轮询和阻塞。
  • 完善客户端断开处理:当客户端出错或断开时,及时关闭socket并从列表中移除,同时广播离开消息。

内容的提问来源于stack exchange,提问作者danilogames220

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:32:20