skynet 为 udp 协议做了有限的支持。和 tcp 协议不同,udp 协议不需要阻塞读取。这是因为 udp 是不可靠协议,无法预期下一个读到的数据包是什么(协议允许乱序和丢包),因此 skynet 的 udp 协议封装采用的是 callback 的方式。
一、服务端测试
main.lua
local skynet = require "skynet"
local socket = require "skynet.socket"
local socket_id = nil
function udp_recv(str, from)
address, port = socket.udp_address(from)
skynet.error("recv udp data from: " .. address .. ":" .. port .. " data:" .. str)
socket.sendto(socket_id, from, string.upper(str))
end
skynet.start(function()
--监听指定地址
socket_id = socket.udp(udp_recv, "192.168.255.128", 8003)
assert(socket_id)
end)
-
socket.udp(function(str, from), address, port) : id
- 参数一: callback 函数,它会收到两个参数。str:收到的包内容,字符串类型;from:消息来源的字符串,可用于后续对本条消息的响应地址(见 socket.sendto)
- 参数二:需要绑定的 ip 地址,字符串,默认为 ipv4 的 0.0.0.0。
- 参数三:绑定的端口,数字,若不写或传 0,表示仅创建一个 udp handle (用于发送),但不绑定固定端口。
-
socket.udp_address(from) : address port
将 from 转换为可读的 ip 地址和端口,用于记录。
测试采用 sokit 作为客户端,测试结果如下:
二、客户端测试
main.lua
local skynet = require "skynet"
local socket = require "skynet.socket"
local socket_id = nil
function client(id)
socket.udp_connect(socket_id, "127.0.0.1", 8003)
local i = 0
while(i < 3) do
socket.write(socket_id, "data"..i)
skynet.error("send data"..i)
i = i + 1
end
socket.close(id)
skynet.exit()
end
function udp_recv(str, from)
address, port = socket.udp_address(from)
skynet.error("recv udp data from: " .. address .. ":" .. port .. " data:" .. str)
--socket.sendto(socket_id, from, string.upper(str))
end
skynet.start(function()
socket_id = socket.udp(udp_recv)
assert(socket_id)
skynet.fork(client, socket_id)
end)
-
socket.udp_connect(id, address, port, callback)
给 id 设置一个默认的发送目的地址,之后就可以用 socket.write 来发送数据包。
- id:socket.udp() 的返回值;
- address/port:默认发送地址,当你用 socket.udp 创建出一个非监听状态的 handle 时,设置目的地址非常有用。因为你很难有别的方法获得一个有效的供 socket.sendto 使用的地址串。
- callback:同 socket.udp() 第一个参数,可选项,通常应该在 socket.udp 创建出 handle 时就设置好 callback 函数。但有时,handle 并不是当前 service 创建而是由别处创建出来的。这种情况,你可以用 socket.start 重设 handle 的所有权,并用这个函数设置 callback 函数。
注:
handle 只能属于一个 service ,当一个 handle 归属一个 service 时,skynet 框架将对应的网络消息转发给它。向一个 handle 发送网络数据包则不需要当前 service 拥有这个 handle 。
采用前一章节的脚本作为服务端,测试结果如下:
客户端:
服务端: