【问题标题】:UDP client and server with Twisted Python使用 Twisted Python 的 UDP 客户端和服务器
【发布时间】:2011-04-07 15:04:39
【问题描述】:

我想创建一个服务器和客户端,使用 Twisted 从网络发送和接收 UDP 数据包。我已经用 Python 中的套接字编写了这个,但想利用 Twisted 的回调和线程特性。但是,我需要 Twisted 设计方面的帮助。

我想要接收多种类型的数据包,但我们假设只有一种:

class Packet(object):
    def __init__(self, data=None):
        self.packet_type = 1
        self.payload = ''
        self.structure = '!H6s'
        if data == None:
            return

        self.packet_type, self.payload = struct.unpack(self.structure, data)

    def pack(self):
        return struct.pack(self.structure, self.packet_type, self.payload)

    def __str__(self):
        return "Type: {0}\nPayload {1}\n\n".format(self.packet_type, self.payload)

我制作了一个协议类(几乎是示例的直接副本),当我从另一个程序发送数据时,它似乎可以工作:

class MyProtocol(DatagramProtocol):
    def datagramReceived(self, data, (host, port)):
        p = Packet(data)
        print p

reactor.listenUDP(3000, MyProtocol())
reactor.run()

我不知道如何创建一个客户端,它可以在网络上发送任意数据包,这些数据包会被反应器接收:

# Something like this:
s = Sender()
p = Packet()
p.packet_type = 3
s.send(p.pack())
p.packet_type = 99
s.send(p.pack())

我还需要确保在客户端和服务器上设置重用地址标志,以便我可以在同一设备上同时运行每个的多个实例(例如,一个脚本正在发送心跳,另一个脚本响应心跳等)。

有人可以告诉我如何使用 Twisted 来实现吗?

更新

这就是我在 Python 中使用套接字的方式。我可以同时运行多个侦听器和发送器,它们都可以互相听到。如何使用 Twisted 获得此结果? (收听部分不必是一个单独的过程。)

class Listener(Process):
    def __init__(self, ip='127.0.0.1', port=3000):
        Process.__init__(self)
        self.ip = ip
        self.port = port

    def run(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind((self.ip, self.port))

        data, from_ip = sock.recvfrom(4096)
        p = Packet(data)
        print p

class Sender(object):
    def __init__(self, ip='127.255.255.255', port=3000):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.ip = (ip, port)

    def send(self, data):
        self.sock.sendto(data, self.ip)

if __name__ == "__main__":
    l = Listener()
    l.start()
    s = Sender()
    p = Packet()
    p.packet_type = 4
    p.payload = 'jake'
    s.send(p.pack())

工作解决方案

class MySender(DatagramProtocol):
    def __init__(self, packet, host='127.255.255.255', port=3000):
        self.packet = packet.pack()
        self.host = host
        self.port = port

    def startProtocol(self):
        self.transport.write(self.packet, (self.host, self.port))

if __name__ == "__main__":
    packet = Packet()
    packet.packet_type = 1
    packet.payload = 'jake'

    s = MySender(packet)

    reactor.listenMulticast(3000, MyProtocol(), listenMultiple=True)
    reactor.listenMulticast(3000, s, listenMultiple=True)
    reactor.callLater(4, reactor.stop)
    reactor.run()

【问题讨论】:

    标签: python udp twisted


    【解决方案1】:

    就像上面的服务器示例一样,还有一个客户端示例。 这应该可以帮助您入门:

    好的,这是一个使用数据报协议的简单心跳发送器和接收器。

    from twisted.internet.protocol import DatagramProtocol
    from twisted.internet import reactor
    from twisted.internet.task import LoopingCall
    import sys, time
    
    class HeartbeatSender(DatagramProtocol):
        def __init__(self, name, host, port):
            self.name = name
            self.loopObj = None
            self.host = host
            self.port = port
    
        def startProtocol(self):
            # Called when transport is connected
            # I am ready to send heart beats
            self.loopObj = LoopingCall(self.sendHeartBeat)
            self.loopObj.start(2, now=False)
    
        def stopProtocol(self):
            "Called after all transport is teared down"
            pass
    
        def datagramReceived(self, data, (host, port)):
            print "received %r from %s:%d" % (data, host, port)
    
    
        def sendHeartBeat(self):
            self.transport.write(self.name, (self.host, self.port))
    
    
    
    class HeartbeatReciever(DatagramProtocol):
        def __init__(self):
            pass
    
        def startProtocol(self):
            "Called when transport is connected"
            pass
    
        def stopProtocol(self):
            "Called after all transport is teared down"
    
    
        def datagramReceived(self, data, (host, port)):
            now = time.localtime(time.time())  
            timeStr = str(time.strftime("%y/%m/%d %H:%M:%S",now)) 
            print "received %r from %s:%d at %s" % (data, host, port, timeStr)
    
    
    
    heartBeatSenderObj = HeartbeatSender("sender", "127.0.0.1", 8005)
    
    reactor.listenMulticast(8005, HeartbeatReciever(), listenMultiple=True)
    reactor.listenMulticast(8005, heartBeatSenderObj, listenMultiple=True)
    reactor.run()
    

    广播示例只是对上面的做法进行了修改:

    from twisted.internet.protocol import DatagramProtocol
    from twisted.internet import reactor
    from twisted.internet.task import LoopingCall
    import sys, time
    
    class HeartbeatSender(DatagramProtocol):
        def __init__(self, name, host, port):
            self.name = name
            self.loopObj = None
            self.host = host
            self.port = port
    
        def startProtocol(self):
            # Called when transport is connected
            # I am ready to send heart beats
            self.transport.joinGroup('224.0.0.1')
            self.loopObj = LoopingCall(self.sendHeartBeat)
            self.loopObj.start(2, now=False)
    
        def stopProtocol(self):
            "Called after all transport is teared down"
            pass
    
        def datagramReceived(self, data, (host, port)):
            print "received %r from %s:%d" % (data, host, port)
    
    
        def sendHeartBeat(self):
            self.transport.write(self.name, (self.host, self.port))
    
    
    
    class HeartbeatReciever(DatagramProtocol):
        def __init__(self, name):
            self.name = name
    
        def startProtocol(self):
            "Called when transport is connected"
            self.transport.joinGroup('224.0.0.1')
            pass
    
        def stopProtocol(self):
            "Called after all transport is teared down"
    
    
        def datagramReceived(self, data, (host, port)):
            now = time.localtime(time.time())  
            timeStr = str(time.strftime("%y/%m/%d %H:%M:%S",now)) 
            print "%s received %r from %s:%d at %s" % (self.name, data, host, port, timeStr)
    
    
    
    heartBeatSenderObj = HeartbeatSender("sender", "224.0.0.1", 8005)
    
    reactor.listenMulticast(8005, HeartbeatReciever("listner1"), listenMultiple=True)
    reactor.listenMulticast(8005, HeartbeatReciever("listner2"), listenMultiple=True)
    reactor.listenMulticast(8005, heartBeatSenderObj, listenMultiple=True)
    reactor.run()
    

    【讨论】:

    • 我在 Google 的帮助下自己找到了这些示例,但它们并没有解决我遇到的问题。
    • @Jake 这是否解决了套接字重用的问题,或者您正在寻找其他东西?
    • +1 这可行,但由于它使用多播,因此只有一个侦听反应器正在接收发送方发出的数据。这让我更接近我正在寻找的东西,它是向所有收听客户的广播。 (对于寻找多播的人,您应该保留此示例!)
    • @Jake 谢谢,我想,我已经理解了所有要求。我留下了上面的代码并添加了修改上面示例的代码,以便向所有监听客户端广播。这是您正在寻找的可接受的答案吗?
    • 谢谢 :) 我将listenMulticast 用于以下属性-listenMultiple=True,它允许地址重用。否则,listenUDP 可以采用的方法没有区别。
    【解决方案2】:

    查看echoclient_udp.py 示例。

    由于 UDP 在客户端和服务器之间几乎是对称的,因此您也只想在那里运行 reactor.listenUDPconnect 到服务器(实际上只是设置发送数据包的默认目的地),然后 transport.write 发送你的数据包。

    【讨论】:

    • 你是否建议我调用 reactor.listenUDP 两次(一次与服务器,一次与客户端)然后调用 reactor.run?我无法尝试,因为我没有设置重用地址,所以我不知道这是否有效。
    • 我建议你在每个套接字上听一次,大概在单独的进程中,然后在每个进程中 reactor.run。您需要为每个进程提供不同的(ip、端口)组合。我不明白reuseaddr 的作用在哪里?
    猜你喜欢
    • 2013-12-24
    • 2020-06-22
    • 1970-01-01
    • 1970-01-01
    • 2013-07-30
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多