socketstcpluatcpserver

lua server can't handle mutiple clients


so, im learning about socket progamming, and im trying to make a simple tcp chatroom server in lua.

It connects fine to a single client, but when aother client tries to join the server, it waits until the other client quits and then it joins

server.lua

require('socket')
server = assert(socket.bind('localhost', 69420))

clients = {}
--data = {} --use later

function broadcast(message)
        if message then
            for _, client in pairs(clients) do
                    client:send(message .. '\n')
            end
    end
end


function handle(client)
    while 1 do
        local buff, err = client:receive()
        if err then
            print(err)
            client:send("error occurred = ".. err)
            client:close()
            break
        else
            print(buff)
            broadcast(buff)
        end
    end
end

function recieve()
    while 1 do
        server:settimeout(0.01) -- tried adding this, still didn't worked
        local client = server:accept()
        if client then
            table.insert(clients, client)

            print("a new client entered")
            broadcast("a new client has joined us\n")

            coroutine.wrap(handle)(client) --this was supposed to handle the clients
        end
    end
end

print("localhost server listening on port 69420... nice")
recieve()

also, im using this client script:

client.lua

-- simple script just to check for server errors 
require('socket')
client = socket.tcp()

client:connect("localhost", 69420)
client:send("hello\n")

while 1 do 
    local s, status, partial = client:receive()
    print(s or partial)
    if status == "closed" then
      break
    end
end

client:close()

Solution

  • I suggest using a multithreaded library, such as Lanes. If you only want to use LuaSocket, you need to follow the order below (pseudocode):

    while 1 do
        server:accept()
        ...
        for i,client in ipairs(clients) do
            client:receive()
            ...
            client:send()
            ...
        end
    end
    

    You can use socket.select to filter out the clients that have already received data or ready to send data, thus avoiding having to loop through all clients each time:

    local recvt, sendt, err = socket.select(clients, clients, 0.01)
    if not err then
        if recvt then
            -- Calling receive on clients in this table will immediately return.
            for k,c in ipairs(recvt) do
                local line, err = c:receive()
            end
        end
        if sendt then
            -- Same as recvt
            for k,c in ipairs(sendt) do
            end
        end
    end