jsp网站开发的环境配置高德地图开发平台
目录
- 一、服务端(Server)通信流程
- 1. 启动监听
- 2. 接受客户端连接
- 3. 接收客户端数据
- 4. 主动关闭连接
- 二、客户端(Client)通信流程
- 1. 连接服务端
- 2. 接收服务端数据
- 3. 主动发送数据
- 4. 断开连接
- 三、完整交互时序图
- 四、关键注意事项
- 1. 非阻塞与协程
- 2. 粘包/分包处理
- 3. 错误处理
- 五、性能优化建议
- 六、示例代码整合
- 服务端完整代码
- 客户端完整代码
以下是基于 Skynet 框架的 Socket 通信流程详解,涵盖服务端与客户端的交互全过程,包括核心 API 使用、数据收发逻辑及示例代码:
一、服务端(Server)通信流程
1. 启动监听
- API:
socket.listen(host, port, backlog)
- 功能: 绑定 IP 和端口,开启监听。
- 参数:
host
: 监听地址(如"0.0.0.0"
表示所有网卡)。port
: 监听端口。backlog
: 等待连接队列的最大长度。
- 返回值: 监听句柄
id
,用于后续操作。
local skynet = require "skynet"
local socket = require "skynet.socket"-- 启动服务端
skynet.start(function()local listen_id = socket.listen("0.0.0.0", 8888, 128)skynet.error("Server listening on port 8888")socket.start(listen_id, accept_handler) -- 注册 accept_handler 处理新连接
end)
2. 接受客户端连接
- API:
socket.start(fd, accept_cb)
- 功能: 将监听句柄
fd
托管给 Skynet,当有新连接时触发accept_cb
。
- 功能: 将监听句柄
- 回调函数:
accept_handler(fd, addr)
- 参数:
fd
: 新连接的客户端 Socket 句柄。addr
: 客户端地址(如"127.0.0.1:12345"
)。
- 参数:
local function accept_handler(client_fd, addr)skynet.error("New client connected:", addr)socket.start(client_fd, receive_handler) -- 注册 receive_handler 处理数据
end
3. 接收客户端数据
- 回调函数:
receive_handler(fd, data)
- 触发条件: 当客户端发送数据时,Skynet 自动调用。
- 参数:
fd
: 客户端 Socket 句柄。data
: 收到的二进制数据(可能不完整)。
- 注意: 需自行处理粘包/分包(参考前文的分包策略)。
local function receive_handler(client_fd, data)skynet.error("Received data from client", client_fd, ":", data)-- 示例:回显数据socket.write(client_fd, "Echo: " .. data)
end
4. 主动关闭连接
- API:
socket.close(fd)
- 功能: 关闭指定 Socket 连接。
-- 在适当位置调用
socket.close(client_fd)
二、客户端(Client)通信流程
1. 连接服务端
- API:
socket.open(host, port)
- 功能: 发起 TCP 连接到服务端。
- 参数: 同
socket.listen
。 - 返回值: 客户端 Socket 句柄
fd
。
local skynet = require "skynet"
local socket = require "skynet.socket"skynet.start(function()local server_fd = socket.open("127.0.0.1", 8888)if server_fd thenskynet.error("Connected to server")socket.start(server_fd, client_receive_handler)-- 发送数据示例socket.write(server_fd, "Hello Server!")elseskynet.error("Connection failed")end
end)
2. 接收服务端数据
- 回调函数:
client_receive_handler(fd, data)
- 逻辑与服务端
receive_handler
类似。
- 逻辑与服务端
local function client_receive_handler(fd, data)skynet.error("Received from server:", data)
end
3. 主动发送数据
- API:
socket.write(fd, data)
- 功能: 向指定 Socket 写入数据。
- 注意: 数据需为字符串格式。
socket.write(server_fd, "Another message")
4. 断开连接
- 与服务端关闭方式相同:
socket.close(server_fd)
三、完整交互时序图
Client Server| ||--- socket.open() ---->| 建立连接| |--触发 accept_handler| ||--- socket.write() ---->| 发送数据| |--触发 receive_handler|<--- socket.write() ----| 服务端响应| ||--- socket.close() ---->| 断开连接
四、关键注意事项
1. 非阻塞与协程
- Skynet 的 Socket 操作是非阻塞的,所有收发操作通过回调异步处理。
- 若需同步等待数据,可在协程中使用
socket.read
:local data = socket.read(fd, len) -- 阻塞协程直到读取 len 字节
2. 粘包/分包处理
- 必须设计应用层协议(如长度前缀、分隔符)确保数据完整性。
- 示例协议解析(长度前缀):
-- 服务端 receive_handler 中 local buffer = {} -- 按 fd 存储缓冲区 function receive_handler(fd, data)if not buffer[fd] then buffer[fd] = "" endbuffer[fd] = buffer[fd] .. datawhile true doif #buffer[fd] < 2 then break end -- 假设长度字段为 2 字节local len = string.unpack(">I2", buffer[fd])if #buffer[fd] >= len + 2 thenlocal packet = string.sub(buffer[fd], 3, len + 2)process_packet(fd, packet)buffer[fd] = string.sub(buffer[fd], len + 3)elsebreakendend end
3. 错误处理
- 监听或连接失败时返回
nil
。 - 连接异常关闭时,回调函数的
data
参数为nil
:function receive_handler(fd, data)if not data thenskynet.error("Connection closed by peer:", fd)socket.close(fd)returnend-- 正常处理数据 end
五、性能优化建议
-
减少内存拷贝:
- 使用
table
存储分片数据,最后用table.concat
合并。
local buffer_tbl = {} function receive_handler(fd, data)buffer_tbl[fd] = buffer_tbl[fd] or {}table.insert(buffer_tbl[fd], data)-- ...解析协议后清理 buffer_tbl end
- 使用
-
连接池管理:
- 对高频短连接场景,复用 Socket 句柄减少开销。
-
协议设计:
- 优先使用二进制协议(如
string.pack
/string.unpack
)减少传输体积。
- 优先使用二进制协议(如
六、示例代码整合
服务端完整代码
local skynet = require "skynet"
local socket = require "skynet.socket"local clients = {} -- 存储客户端 fdlocal function process_packet(fd, packet)skynet.error("Process packet from", fd, ":", packet)socket.write(fd, "ACK: " .. packet)
endlocal function receive_handler(fd, data)if not data thenskynet.error("Client disconnected:", fd)socket.close(fd)clients[fd] = nilreturnend-- 假设协议为长度前缀(2字节)if not clients[fd] thenclients[fd] = { buffer = "" }endclients[fd].buffer = clients[fd].buffer .. datawhile true doif #clients[fd].buffer < 2 then break endlocal len = string.unpack(">I2", clients[fd].buffer)if #clients[fd].buffer >= len + 2 thenlocal packet = string.sub(clients[fd].buffer, 3, len + 2)process_packet(fd, packet)clients[fd].buffer = string.sub(clients[fd].buffer, len + 3)elsebreakendend
endlocal function accept_handler(fd, addr)skynet.error("New client:", addr)clients[fd] = { buffer = "" }socket.start(fd, receive_handler)
endskynet.start(function()local listen_fd = socket.listen("0.0.0.0", 8888, 128)socket.start(listen_fd, accept_handler)skynet.error("Server started on 8888")
end)
客户端完整代码
local skynet = require "skynet"
local socket = require "skynet.socket"skynet.start(function()local fd = socket.open("127.0.0.1", 8888)if not fd thenskynet.error("Connect failed")returnendsocket.start(fd, function(fd, data)if not data thenskynet.error("Server closed")returnendskynet.error("Received:", data)end)-- 发送带长度前缀的数据包local msg = "Hello World"local len = #msglocal packed = string.pack(">I2", len) .. msgsocket.write(fd, packed)
end)
通过以上流程,可清晰理解 Skynet 中 Socket 通信的完整生命周期。实际开发中需根据业务需求调整协议解析和错误处理逻辑。