我正在制作一个网关服务器,用于在设备和后端之间发送和接收数据。我必须使用 UDP 与设备通信,因为这是一项已实现的技术。这些设备具有固定类型的消息,我无法修改。我的服务器正在使用公共 IP。
我想知道是否可以使用 UDP 将数据发送到位于其他网络中的特定设备,而无需创建套接字。我只知道网络的公共IP和设备的MAC。
目前,我已经使用Twisted实现了一个脚本,可以发送和接收数据,该模块用于监控设备。我想要另一个使用 FastAPI 的模块,它管理请求并使用 UDP(带有套接字库)发送数据。这两个模块单独工作正常。第二个模块在本地网络中工作正常,但我希望它能够与不同网络中的设备一起工作。
第一个脚本(它有效,它不是整个代码)
class Server(DatagramProtocol):
'''Datagram Protocol for UDP connections.'''
def __init__(self):
self.UDPconnections = dict() #DICT --> JSON --> REDIS
self.transport_devices = dict()
self.cont = 0
self.comandos = sc()
self.id_placa = 0
self.client = redis.Redis(host='xx.xx.xx.xx', port=xxx, db=0)
def datagramReceived(self, data, addr):
data_hex = data.hex()
print(f"received {data_hex} ({type(data)}) from {addr}")
self.cont = self.cont+1
match data_hex:
case CONST.PLACA_PON:
cmd = self.comandos.KeepAliveServidor()
case CONST.PLACA_VIVA:
if self.UDPconnections:
#No vacio
res = ipExists(self.UDPconnections, "ip")
if str(addr[0]) not in res:
cmd = self.comandos.ConfigurarPlaca()
print(f"data: {cmd.hex()}")
else:
cmd = self.comandos.KeepAliveServidor()
else:
#VACIO: recibo parametros de conf de data (inicial)
print("dict inicial")
cmd = self.comandos.ConfigurarPlaca()
case _:
'''code block'''
try:
self.transport.write(cmd, addr)
print(f'Send: {cmd.hex()} to {addr}')
except NameError as e:
log.err("cmd not asigned: {}".format(str(e)))
def StartServer():
hostname=socket.gethostname()
IPAddr = "0.0.0.0"
log.startLogging(open("serverUDP.log", "w"))
# UDP------------------------------------------
portSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
portSocket.setblocking(False)
portSocket.bind((IPAddr, 5002))
port = reactor.adoptDatagramPort(portSocket.fileno(), socket.AF_INET, Server())
portSocket.close()
reactor.run()
if __name__ == '__main__':
print("main")
StartServer()
第二个模块是这样的:
import asyncio
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
import socket
# 1. Define data type
class Msg(BaseModel):
msg: str
mac: str
add: str
# 2. Define an API object
app = FastAPI()
# 3. Map HTTP method and path to python function
@app.post("/send-udp/")
async def send_udp_message(inp: Msg):
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.setblocking(False)
try:
#Mensaje de apertura
cmd_send = find_method(inp.msg)
udp_socket.sendto(cmd_send, (inp.add, 5002))
desired_response = "xxxx"
timeout = 5
response_message = await receive_response(udp_socket, desired_response, timeout)
return {"message": "UDP message sent successfully", "response": response_message}
except Exception as e:
return {"error": str(e)}
finally:
udp_socket.close()
if __name__=="__main__":
uvicorn.run('main:app', host='0.0.0.0', port=8000, reload=True)
第一种方法是通过 UDP 接收数据,将该数据保存在 REDIS 中,然后使用 REDIS 中的设备数据作为 JSON(MAC、本地 IP 地址、网关地址等)。我知道 Redis 并不持久,但在这种情况下可以为我工作。
当我想向特定客户端发送数据时,问题出现了。我想我可以将套接字保存在字典中,例如,在收到数据后。例如,我可以在 Twisted 模块中执行此操作。
这是在两个模块之间共享套接字字典的一种方法,还是最好在同一个模块中完成所有任务?据我所知,如果我序列化套接字对象,将其保存在 Redis 中,然后再次反序列化,我将丢失套接字。
我只知道网络的公网IP和设备的MAC
MAC 根本没有帮助,因为它只能作为同一本地网络内的地址。
只要路由器没有将此公共 IP 和端口映射到特定的内部 IP 地址和端口,公共 IP 本身就没有帮助。如果没有这样的映射,路由器不知道应该将收到的数据转发到哪里,因此只会丢弃它。
这样的映射可能是显式添加到路由器的端口转发。可能是 UPnP 添加的一些转发。最简单的方法可能是内部程序将一些数据发送到外部应用程序,因为这会在路由器中创建一段时间的响应数据状态。但这些都不能由外部程序完成——它总是需要内部的帮助。