实战演练1:普通UDP
UDP是一种无连接对等通信协议,没有服务器和客户端概念,通信的任何一方均可通过通信原语直接和其他方通信
1、相对于TCP,UDP编程只需定义DatagramProtocol子类 无需定义Factory;完全基于twisted的方案
from twisted.internet.protocol import DatagramProtocolfrom twisted.internet import reactorimport threadingimport timeimport datetimeclass Echo (DatagramProtocol): # Protocol子类,此处进行通信逻辑开发 def datagramReceived(self, datagram, addr): print("got data from %s" % addr) print(datagram.decode('utf8'))protocol = Echo()host = '127.0.0.1'port = 8007bStop = Falsedef routine(): # 每隔5秒向服务器发送消息 while not bStop: # 第一个参数是发送的内容,第二个参数是 发送目的地的ip和端口号 protocol.transport.write (('hello,i am %s' % (datetime.datetime.now ())).encode ('utf8'), (host, port)) time.sleep (5)threading.Thread (target=routine).start () # 启动县城运行routine()函数reactor.listenUDP (port, protocol)# 传入端口地址和处理该端口数据的DatagramProtocol子类reactor.run () # 挂起运行bStop = True # 通知routine线程退出
2、适配普通的socket对象的UDP编程
有时需要利用在其他模块中已经建立好的socket对象进行UDP编程,而无法完全基于twisted的方案
from twisted.internet.protocol import DatagramProtocolfrom twisted.internet import reactorimport socketclass Echo (DatagramProtocol): # DatagramProtocol子类 def datagramReceived(self, datagram, addr): print("got data from %s" % addr) print(datagram.decode('utf8'))protocol = Echo()host = '127.0.0.1'port = 8007# 建立普通socket对象portSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)portSocket.setblocking(False) # 设为阻塞模式portSocket.bind((host,port))reactor.adoptDatagramPort(portSocket.fileno(),socket.AF_INET,protocol) # 适配普通socketportSocket.close() # 在启动reactor之前关闭普通socket对象reactor.run()
实战演练2:Connect UDP
虽然UDP本身是无连接协议,但是编程接口仍然可以调用connect()函数,用来限制只与某地址和端口通信,当调用connect()函数,当需要向该地址发送数据时就不需要指定目标端口和地址了
Connect UDP本质上是数据报协议,虽然一定程度上实现了点对点链接
UDP | Connected UDP | TCP | |
是否是点对点通信 | 否 | 是 | 是 |
数据包质检是否有序 | 否 | 否 | 是 |
发送是否可靠(发送方是否知晓数据已到达) | 否 | 是 | 是 |
是否支持广播,组播 | 是 | 否 | 否 |
用Connected UDP改造后的UDP通信代码示例:
from twisted.internet.protocol import DatagramProtocolfrom twisted.internet import reactorimport threading,time,datetimehost = '127.0.0.1'port = 8007class Echo (DatagramProtocol): # DatagramProtocol子类 def startProtocol(self): # 连接成功后被调用 self.transport.connect(host,port) # 指定对方的地址和端口 print('连接已经创建') def datagramReceived(self, datagram, addr): # 收到数据时被调用 print(datagram.decode('utf8')) def connectionRefused(self): # 每次通信失败后调用 print('发送失败') def stopProtocol(self): print('连接关闭')protocol = Echo()bStop = Falsedef routine(factory): # 每隔5秒向服务器发送消息 while not bStop: # 发送数据时只需传入数据,无需传入对方地址和端口 protocol.transport.write (('hello,i am %s' % (datetime.datetime.now ())).encode ('utf8')) time.sleep (5)threading.Thread (target=routine,args=(factory,)).start () # 启动县城运行routine()函数reactor.listenUDP (port, protocol)# 传入端口地址和处理该端口数据的DatagramProtocol子类reactor.run () # 挂起运行bStop = True # 通知routine线程退出
实战演练3:组播技术
在IPv4中 224.0.0.0 ~ 239.255.255.255 这个范围被用于组播管理,参与者在实际收发数据之前需要加入该地址范围中的一个ip地址,之后所有终端都可以用UDP的方式向组中的其他终端发送消息
twisted 中的组播编程代码示例如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-from twisted.internet.protocol import DatagramProtocolfrom twisted.internet import reactormulticast_ip = '224.0.0.1' # 组播地址port = 8001 # 端口class Multicast (DatagramProtocol): # DatagramProtocol子类 def startProtocol(self): # 连接成功后被调用 self.transport.joinGroup(multicast_ip) # 加入组播组 self.transport.write(('Notify').encode('utf8'),(multicast_ip,port)) # 组播数据 def datagramReceived(self, datagram, addr): # 收到数据时被调用 print('datagram %s received from %s' % (repr(datagram),repr(addr))) if datagram =='Notify': self.transport.write(('Acknowlege').encode('utf8'),addr) # 单播回应 reactor.listenMulitcast(port,Multicast(),listenMultiple = True) # 组播监听reactor.run() # 挂起运行
与joinGroup对应的,还有leaveGroup退出组播