使用Twisted Python的UDP客户端和服务器

时间:2010-09-03 00:15:26

标签: python udp twisted

我想创建一个服务器和客户端,使用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()

2 个答案:

答案 0 :(得分:12)

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

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

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()

答案 1 :(得分:2)

查看echoclient_udp.py示例。

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