2010-09-03 97 views
6

我想創建一個服務器和客戶端,使用Twisted從網絡發送和接收UDP數據包。我已經用Python中的套接字編寫了它,但是想要利用Twisted的回調和線程功能。不過,我需要幫助,但Twisted的設計。UDP客戶端和服務器與扭曲的Python

我有多種類型我想收到的數據包,但是讓我們假設只有一個:

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

回答

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

我在Google的幫助下自己找到了這些示例,但它們沒有解決我遇到的問題。 – Jake 2010-09-03 01:35:04

+0

@Jake這是否解決套接字重用問題或者您正在尋找其他東西? – pyfunc 2010-09-03 07:06:48

+0

+1這有效,但由於它使用多播,所以只有一個監聽反應器正在接收發送者正在發送的數據。這讓我更接近我所尋找的東西,這是對所有聽客戶的廣播。 (你應該保留這個例子,以供尋找多播的人使用) – Jake 2010-09-03 15:02:18

1

檢查出echoclient_udp.py例子。因爲UDP在客戶端和服務器之間幾乎是對稱的,所以你只需要在那裏運行reactor.listenUDPconnect給服務器(它真的只是設置發送數據包的默認目的地),然後transport.write發送你的數據包。

+0

你是在暗示我打電話reactor.listenUDP兩次(與服務器一次,一旦與客戶端),然後調用reactor.run?我無法嘗試,因爲我沒有設置複用地址,所以我不知道這是否真的有效。 – Jake 2010-09-03 01:32:42

+0

我建議你在每個socket上監聽一次,大概是在單獨的進程中,然後在每個進程中運行'reactor.run'。您需要爲每個進程都有獨特的(ip,port)組合。我不明白reuseaddr在什麼地方進來了? – poolie 2010-09-03 01:48:59