lieudusty
lieudusty

Reputation: 23

Multiple connections Lua Sockets

I am making an IRC like chat client and server using Lua Sockets and TCP. The main thing I'm trying to figure out is how to make the client and server listen for messages and send them at the same time. Since when doing socket:accept() on the server it stalls the program until a connection is created. Is there a way to accept multiple connections and store them into a table?

Upvotes: 2

Views: 2929

Answers (2)

Duc Vu
Duc Vu

Reputation: 95

You will need to settimeout before accept() to allow non-blocking socket. According to the documentation of Lua socket, accept() method will block next connections.

By default, all I/O operations are blocking. That is, any call to the methods send, receive, and accept will block indefinitely, until the operation completes. The settimeout method defines a limit on the amount of time the I/O methods can block

The following is an working demo of chat server. You can use telnet to connect to it.

socket = require("socket") -- import lib

-- define host and port
host = "*"
port = 8080

-- create socket and bind to an available port
server = assert(socket.bind(host, port))

-- get local port number and IP
ip, port = server:getsockname()

totalclient = 0 -- store the total connections
clients = {} -- e.g. clients[1] = 0xSocketAddress
clientnames = {} -- e.g. clientnames[1] = "john"

-- start the loop to listening connection
while true do

  -- new client connected, settimeout to not block connetcion
  server:settimeout(0.01)
  local client, err = server:accept();

  -- print the info of new client and add new client to the table
  if (not err) then
    clientname = client:receive()
    totalclient = totalclient + 1
    clients[totalclient] = client
    clientnames[totalclient] = clientname
    print(">> "..clientname.." connected from " .. tostring(client:getsockname()) .." at " ..os.date("%m/%d/%Y %H:%M:%S"))
  end

  -- loop through the client table
  for i = 1, totalclient do
    -- if there is client, listen to that client
    if (clients[i] ~= nil) then
      clients[totalclient]:settimeout(0.01) -- prevent blocking
      clientmessage, err = clients[i]:receive() -- accept data from client

      -- check if there is something sent to server, and no error occured
      if (err == nil and clientmessage ~= nil) then

        -- loop through the list of client to send broadcast message
        else
          for j = 1, totalclient do
            -- check not to send broadcast to the speaker
            if clients[i] ~= clients[j] then
              clients[j]:send(tostring("["..clientnames[i]).."]: " ..clientmessage.."\r\n")
            end
          end--for
          logdbmessage(clientnames[i], clientmessage)
        end--if


      end--if

    end--if
  end--for

end--while

Upvotes: 0

catwell
catwell

Reputation: 7020

This looks exactly like the problem solved by a dispatcher like Copas. You should read this: http://keplerproject.github.com/copas/manual.html#why - even if you don't want to use Copas it will help you figure out how to solve that problem.

Basically, you need to use select() before accept(). Note that even when you do that it is actually not guaranteed that accept() will return immediatly so you should also use settimeout() (see http://w3.impa.br/~diego/software/luasocket/socket.html#select)

Upvotes: 4

Related Questions