Reputation: 23
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
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
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