【问题标题】:Luasocket - How to send/receive UDP broadcast messages?Luasocket - 如何发送/接收 UDP 广播消息?
【发布时间】:2018-02-27 22:40:39
【问题描述】:

我一直在尝试这样做。我尝试关注此http://lua.2524044.n2.nabble.com/UDP-Broadcast-td3995269.html,但无济于事。我已经成功地在客户端和服务器之间发送了 单播 消息。我已经确认我的广播地址是正确的。我唯一能想到的另一件事是嗅探我的界面以确保没有网络问题。我明天会解决这个问题,但想在这里发布这个,看看是否有人会注意到一个愚蠢的错误。

注意:我在同一台笔记本电脑上运行客户端和服务器。

客户

local socket = require("socket")
local s_address, s_port = "192.168.1.135", 33333
local d_address, d_port = "192.168.1.255", 22222

udp = socket.udp()
assert(udp)
assert(udp:settimeout(1))
assert(udp:setoption('broadcast', true))
assert(udp:setoption('dontroute',true))
assert(udp:setsockname(s_address, s_port))

while true do
  tosend = string.format("client %s:%s", s_address, s_port)
  print(string.format('sending to %s:%s...', d_address, d_port))
  udp:sendto(tosend, d_address, d_port)

  data, msg = udp:receive()
  toprint = string.format("data = \"%s\"", data)
  print(toprint)
  socket.sleep(.5)
end

服务器

local socket = require("socket")
local s_address, s_port = "192.168.1.135", 11111
local p_address, p_port = "192.168.1.255", 22222

udp = socket.udp()
assert(udp)
assert(udp:setoption('broadcast', true))
assert(udp:setoption('dontroute',true))
assert(udp:settimeout(1))
assert(udp:setsockname(s_address, s_port))
assert(udp:setpeername(p_address, p_port))

while true do
  data = udp:receive()
  print(string.format('listening on %s:%s...', p_address, p_port))
  toprint = string.format("data = \"%s\"", data)
  print(toprint)
  if data then
    msg_back = "server received your message"
    udp:send(msg_back)
  end
  socket.sleep(.5)
end

输出
双方不断打印data = "nil"。

【问题讨论】:

  • 如果你想在两个设备之间连接,为什么要使用广播,它旨在中断局域网上的每个主机。这似乎是对广播的滥用。如果你想中断每个主机,只使用广播,否则你应该使用单播或多播(对于多个主机)。
  • @RonMaupin 我想与多个主机通信,但现在我正在使用单个客户端和服务器进行测试(为简单起见)。
  • 对,但是多播是您应该为一组多台主机使用的。广播适用于每个主机,而且大多数情况下被滥用,这也是 IPv6 没有广播的原因之一。

标签: networking lua udp broadcast luasocket


【解决方案1】:

我一直在寻找自己实现 luasocket 广播,这是互联网上为数不多的甚至提出这个话题的页面之一。最后,在 luasocket API 和我能够适应的一些 python 代码之间,我找到了一个适合我的解决方案:

local socket = require 'socket'
local HOME_ADDR = '192.168.0.1'
local SUBNET_PATTERN = '%d+%.%d+%.%d+%.'

local function get_own_address()
  local s = assert(socket.udp())
  assert(s:setpeername(HOME_ADDR, 80))
  local host = s:getsockname()
  s:close()
  return host
end

function broadcast(message, address, port)
  local broadcaster = assert(socket:udp())
  assert(broadcaster:setoption('broadcast', true))
  assert(broadcaster:setoption('dontroute', true))   -- do we need this?

  print(('Broadcasting %q to %s:%i'):format(message, address, port))
  assert(broadcaster:sendto(message, address, port))
  broadcaster:close()
end

function antiphony(message, timeout, call_port, response_port, response_mask)
  local message, timeout, mask = tostring(message), tonumber(timeout) or 1.5, response_mask or '*a'
  local send_port, recv_port = call_port or 10500, response_port or 10501
  local broadcast_address = get_own_address():match(SUBNET_PATTERN) .. '255'
  local listener = assert(socket.bind('*', recv_port, 64))
  local clients, responses, starttime = {}, {}

  listener:settimeout(0)
  broadcast(message, broadcast_address, send_port)
  starttime = socket.gettime()
  repeat clients[listener:accept() or false] = true
  until socket.gettime() - starttime > timeout

  for client in pairs(clients) do 
    if client then
      responses[client:getpeername()] = tostring(client:receive(mask))
      client:close()
    end
  end
  listener:close()

  return responses
end

function listen(response_callback, timeout, listen_port, reply_port)
  local recv_port, send_port = tonumber(listen_port) or 10500, tonumber(reply_port) or 10501
  local callback = response_callback or function()end
  local address = get_own_address():match(SUBNET_PATTERN) .. '255'
  local sleeper = socket.udp()

  sleeper:settimeout(timeout)
  sleeper:setsockname(address, recv_port)
  repeat
    local data, ip, port = sleeper:receivefrom(1024)
    if data then callback(ip, send_port, data) end
  until not data
end

function respond(message, data_handler) 
  return function(ip_addr, port, data)
    assert(ip_addr and port, ('Invalid address: %s:%s'):format(tostring(ip_addr), tostring(port)))
    local msg = tostring(type(data_handler) == 'function' and (data_handler(data, message) or messsage) or message)
    local sender = assert(socket.connect(ip_addr, port))
    assert(sender:send(msg))
    sender:close()
  end
end

--[[

-- Listener / Responder:
listen(respond('polo', print), 600) -- 10 minute timeout

-- Broadcaster / Receiever:
for address, response in pairs(antiphony('marco')) do 
  print(address, response) 
end

]]

【讨论】:

  • 请记住,广播会中断 LAN 上的 每个 主机,通过将其发送到网络堆栈来检查广播,以查看主机是否对广播感兴趣,包括路由器之类的东西、打印机等对广播不感兴趣的设备。今天的大多数企业会拒绝使用广播的应用程序,因为它根本不安全,会产生安全风险并浪费 LAN 上的带宽。 IPv6 消除了广播,因此无法将广播应用程序移植到 IPv6。在本世纪实现这一目标的现代方式是使用多播。
  • @RonMaupin :是的,但是如果您必须在本地子网上发现未知数量的嵌入式设备,这些设备设置为响应广播但没有多播功能并且您拥有的唯一工具用lua来做,这是怎么做的。
猜你喜欢
  • 2012-06-05
  • 2011-04-23
  • 2019-05-12
  • 2021-12-18
  • 2020-02-21
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多