0Pricing
Lua Academy · 课时

构建服务器

接受传入连接。

构建服务器 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. TCP 基础
  2. 构建客户端
  3. 构建服务器
  4. HTTP 请求
← 返回 Lua Academy