You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
2.4 KiB
Lua
93 lines
2.4 KiB
Lua
--[[
|
|
-- Simplest possible tcp echo server.
|
|
-- ... Well, that's a lie. It's the simplest possible TCP server that can handle multiple clients at the same time, that I was able to write.
|
|
]]--
|
|
|
|
local socket = require("socket")
|
|
local server = socket.tcp()
|
|
-- The low timeout time is so no one client blocks any other
|
|
-- I can see that if the server needs to receive or send a lot of data, then those routines will become blocked. I _could_ use something better than coroutines to make this, but then it wouldn't have been so simple, now would it? :þ
|
|
server:settimeout(0.005, "t")
|
|
|
|
hostname = "localhost"
|
|
port = 12346
|
|
|
|
server:bind(hostname, port)
|
|
server:listen(128)
|
|
|
|
local clients = {}
|
|
|
|
co_newClient = coroutine.create(function ()
|
|
while true do
|
|
local nC, err = server:accept()
|
|
if err then
|
|
if err == "timeout" then
|
|
coroutine.yield()
|
|
else
|
|
error("ERROR: "..err)
|
|
end
|
|
end
|
|
if nC then
|
|
-- Individual clients, however, need a higher timeout if we want to be able to give them time to connect to the tcp socket and actually send something.
|
|
nC:settimeout(10)
|
|
-- Insert the new client into the client table
|
|
table.insert(clients, nC)
|
|
nC = nil
|
|
coroutine.yield()
|
|
end
|
|
end
|
|
end)
|
|
co_selectClient = coroutine.create(function ()
|
|
while true do
|
|
-- For more information, see https://lunarmodules.github.io/luasocket/socket.html#select
|
|
r, w, e = socket.select(clients, nil, 0.005)
|
|
if e then
|
|
-- Timeout = No clients, keep waiting for clients
|
|
if e == "timeout" then
|
|
coroutine.resume(co_newClient)
|
|
else
|
|
error("ERROR: "..e)
|
|
end
|
|
end
|
|
coroutine.yield()
|
|
end
|
|
end)
|
|
|
|
co_receiveFromClient = coroutine.create(function ()
|
|
while true do
|
|
for _, client in ipairs(r) do
|
|
local message, err, partial = client:receive("*l")
|
|
if err then
|
|
-- Maybe timeout should remove the client from the table too?
|
|
if err == "timeout" then
|
|
coroutine.yield()
|
|
elseif err == "closed" then
|
|
table.remove(clients, _)
|
|
coroutine. yield()
|
|
end
|
|
end
|
|
if message then
|
|
if message == "goodbye" then
|
|
client:send("GOODBYE\n")
|
|
client:close()
|
|
table.remove(clients, _)
|
|
coroutine.yield()
|
|
else
|
|
client:send("Echo: "..message.."\n")
|
|
coroutine.yield()
|
|
end
|
|
end
|
|
if partial then
|
|
coroutine.yield()
|
|
end
|
|
end
|
|
coroutine.yield()
|
|
end
|
|
end)
|
|
|
|
while true do
|
|
coroutine.resume(co_newClient)
|
|
coroutine.resume(co_selectClient)
|
|
coroutine.resume(co_receiveFromClient)
|
|
end
|