构建服务器
接受传入连接。
构建服务器 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。
服务器生命周期
TCP 服务器会绑定地址、监听连接,然后循环接受连接并为客户端提供服务。
在 LuaSocket 中,您可以使用 socket.tcp() 创建主对象,使用 bind/listen 将其变成监听服务器,再使用 accept 从队列中取出客户端。
local socket = require("socket")
local server = assert(socket.tcp())绑定端口
server:bind(host, port) 会保留一个本地地址。使用 "*" 或 "0.0.0.0" 可以接受所有网络接口上的连接,使用 "127.0.0.1" 则仅接受本地连接。
如果另一个进程占用了该端口,绑定会失败并返回 "address already in use"。
server:setoption("reuseaddr", true)
local ok, err = server:bind("*", 8080)
if not ok then error("bind failed: " .. err) end监听客户端
server:listen(backlog) 会将套接字设为被动状态,并开始将传入的握手请求加入队列。backlog 是待处理连接的最大数量。
快捷方式 socket.bind(host, port, backlog) 会在一次调用中完成绑定和监听,并返回一个已就绪的服务器对象。
local socket = require("socket")
local server = assert(socket.bind("*", 8080, 32))
print("listening on", server:getsockname())接受连接
server:accept() 会一直阻塞,直到客户端连接,然后为该对端返回一个全新的 TCP 客户端对象。服务器套接字本身会继续监听。
对于设置了超时的服务器套接字,accept 会返回 nil, "timeout",这样您就可以穿插执行其他工作。
local client, err = server:accept()
if client then
print("client from", client:getpeername())
end为单个客户端提供服务
接受连接后,请将返回的对象完全当作客户端套接字使用:使用 receive 接收请求,使用 send 发送响应。
为每个客户端套接字设置独立的超时,以免一个缓慢的客户端阻塞整个服务器。
client:settimeout(10)
local line = client:receive("*l")
if line then client:send(line .. "\n") end
client:close()接受循环
经典的迭代式服务器会无限循环:接受一个客户端,为其提供服务,关闭连接,然后重复执行。这种方式一次处理一个客户端。
这种方式简单且正确,但无法服务并发客户端,因为一个缓慢的请求会阻塞排在其后的所有客户端。
while true do
local c = server:accept()
if c then
local msg = c:receive("*l")
if msg then c:send(msg .. "\r\n") end
c:close()
end
end最简回显服务器
组合起来,回显服务器会绑定、监听,然后循环将每个客户端的第一行原样返回。
这是最小的完整 LuaSocket 服务器,也是对您之前构建的客户端很有用的目标。
local socket = require("socket")
local s = assert(socket.bind("*", 8080))
while true do
local c = s:accept()
c:send((c:receive("*l") or "") .. "\r\n")
c:close()
end使用 select 实现并发
如需在不使用线程的情况下同时服务许多客户端,请使用 socket.select。它会返回准备好读取的套接字,其中包括有新客户端等待时的监听服务器套接字。
您需要维护一个活动客户端的 table,并且只处理有数据的客户端,从而保持单线程事件循环的响应能力。
local readable = socket.select(watch, nil, 1)
for _, sock in ipairs(readable) do
if sock == server then
table.insert(watch, server:accept())
end
end非阻塞接受
设置 server:settimeout(0),这样接受连接就不会阻塞。将它与 select 配合使用,以便仅在监听套接字确实可读时调用 accept。
这是可扩展的单进程服务器的基础,它可以对数百个连接进行多路复用。
server:settimeout(0)
local ready = socket.select({server}, nil, 5)
if #ready > 0 then
local c = server:accept()
end处理客户端错误
客户端可能会突然断开。当 receive 返回 nil, "closed" 或部分缓冲区时,请从监视 table 中移除该客户端,并关闭其套接字。
切勿让一个损坏的连接拖垮接受循环;请隔离每个客户端的故障。
local data, err = client:receive("*l")
if err == "closed" then
client:close()
remove_from_watch(client)
end干净关闭
关闭时,请停止接受连接,关闭每个活动客户端套接字,然后关闭监听服务器以释放端口。
在绑定时设置 reuseaddr,可以让您立即重启,而无需等待 TCP TIME_WAIT 时段结束。
for _, c in ipairs(clients) do c:close() end
server:close()
print("server stopped")快速检查
选择能将全新的主套接字变成可用监听器的调用顺序。
回顾
您构建了一个 TCP 服务器:使用 reuseaddr 进行绑定,使用 backlog 进行监听,并循环调用 accept 为客户端提供服务。
您了解了简单的迭代循环,然后使用 socket.select 将其扩展为支持非阻塞并发,同时实现错误隔离和干净关闭。接下来,您将进入 HTTP。
常见问题解答
「构建服务器」课时是免费的吗?
是的 — 「构建服务器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。
「构建服务器」这节课中我会学到什么?
接受传入连接。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Lua Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「构建服务器」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Lua Academy 课中编写并运行代码吗?
能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。