使用UDP发送数据到特定客户端

问题描述 投票:0回答:1

我正在制作一个网关服务器,用于在设备和后端之间发送和接收数据。我必须使用 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 中,然后再次反序列化,我将丢失套接字。

python network-programming udp fastapi twisted
1个回答
0
投票

我只知道网络的公网IP和设备的MAC

MAC 根本没有帮助,因为它只能作为同一本地网络内的地址。

只要路由器没有将此公共 IP 和端口映射到特定的内部 IP 地址和端口,公共 IP 本身就没有帮助。如果没有这样的映射,路由器不知道应该将收到的数据转发到哪里,因此只会丢弃它。

这样的映射可能是显式添加到路由器的端口转发。可能是 UPnP 添加的一些转发。最简单的方法可能是内部程序将一些数据发送到外部应用程序,因为这会在路由器中创建一段时间的响应数据状态。但这些都不能由外部程序完成——它总是需要内部的帮助。

© www.soinside.com 2019 - 2024. All rights reserved.