6.Twisted UDP Socket 编程示例
示例:客户端每隔10秒向服务端报送设备使用情况,具体数据如下:
数据 | 说明 | 值示例 |
CPUUsed | CPU占用情况 % | 35.5% |
MemTotal | 总内存 MB | 2048M |
MemFree | 可用内存 MB | 1024M |
MemUsed | 内存占用情况 % | 50% |
数据以文本进行传输,传输格式:Key:Value,中间用冒号分隔。服务端只做数据的原样显示并回复”OK!”
由于 UDP 没有连接的概念,所以大多数情况下,采用 UDP Socket 实现的应用都是由设备采集数据上报到服务上来运行的。
6.1 UDP 服务端编程
虽然对于 UDP 来说没有服务端和客户端的概念,但在实际项目开发中,我们一般还是要区分服务端和客户端的。对于 UDP 服务端来说,其实代码很简单,因为其主要任务就是接收数据,然后对数据进行处理。
先来编写代码框架:
# coding: utf-8from twisted.internet import reactorfrom twisted.internet.protocol import DatagramProtocolclass Server(DatagramProtocol): def startProtocol(self): pass def datagramReceived(self, data, addr): pass def stopProtocol(self): passreactor.listenUDP(8085, Server())reactor.run()
代码中,主要实现的是 datagramReceived 事件,一般在该事件的代码中就是我们的业务逻辑,示例代码如下:
def startProtocol(self): print(‘Server start.’) def datagramReceived(self, data, addr): print(“received data from %s:%s” % (addr, data)) self.transport.write(‘OK!’.encode(‘utf-8’), addr) def stopProtocol(self): print(‘Server stop.’)
6.2 UDP 客户端编程
客户端代码相对比服务端要复杂一点,主要是客户端需要执行定时任务,这样的话,我们需要设计一个线程来执行该定时任务,由于线程中需要发送数据到服务端,所以,在线程中需要设计一个属性指向客户端的 Protocol。线程的框架代码如下:
# coding: utf-8from twisted.internet import reactorfrom twisted.internet.protocol import DatagramProtocolfrom threading import Threadimport timeimport psutilclass DataThread(Thread): def __init__(self, protocol): self.protocol = protocol self.running = True super().__init__() def run(self): self.running = True pass def stop(self): self.running = False
客户端框架代码:
class Client(DatagramProtocol): def startProtocol(self): pass def datagramReceived(self, data, addr): pass def connectionRefused(self): pass def stopProtocol(self): passclient = Client()thread = DataThread(client)thread.start()reactor.listenUDP(0, client)reactor.run()thread.stop()
线程主要通过 Protocol 发送数据,实现代码如下:
def run(self): self.running = True while True: if not self.running: break time.sleep(10) data = psutil.virtual_memory() total = ‘MemTotal:%0.2f’ % (data.total / 1024) # 总内存 print(total) if self.protocol is not None: self.protocol.transport.write(total.encode(‘utf-8’)) time.sleep(1) free = ‘MemFree:%0.2f’ % (data.available / 1024) # 可用内存 print(free) if self.protocol is not None: self.protocol.transport.write(free.encode(‘utf-8’)) time.sleep(1) memory = “MemUsed:%d” % (int(round(data.percent))) + “%” # 内存占用情况 print(memory) if self.protocol is not None: self.protocol.transport.write(memory.encode(‘utf-8’)) time.sleep(1) cpu = “CPUUsed:%0.2f” % psutil.cpu_percent(interval=1) + “%” # CPU占用情况 print(cpu) if self.protocol is not None: self.protocol.transport.write(cpu.encode(‘utf-8’))
客户端 Protocol 代码实现如下:
def startProtocol(self): host = “127.0.0.1” port = 8085 print(‘Connect to %s:%s’ % (host, port)) self.transport.connect(host, port) def datagramReceived(self, data, addr): print(“received data from %s:%s” % (addr, data)) def connectionRefused(self): print(“No one listening”) def stopProtocol(self): print(‘Stop’)
在 Protocol 启动时连接到服务器,这样就可以形成 Connected UDP,相对于 UDP 来说,数据的传输可靠性有所提高,同时,在代码中发送数据也不需要写服务端的地址。
6.3 运行效果
服务端:
Server start.received data from (‘127.0.0.1’, 56768):b’MemTotal:20880432.00’received data from (‘127.0.0.1’, 56768):b’MemFree:13308280.00’received data from (‘127.0.0.1’, 64355):b’MemTotal:20880432.00’received data from (‘127.0.0.1’, 64355):b’MemFree:13315952.00’received data from (‘127.0.0.1’, 64355):b’MemUsed:36%’received data from (‘127.0.0.1’, 64355):b’CPUUsed:1.20%’Server stop.
客户端:
Connect to 127.0.0.1:8085MemTotal:20880432.00received data from (‘127.0.0.1’, 8085):b’OK!’MemFree:13315952.00received data from (‘127.0.0.1’, 8085):b’OK!’MemUsed:36%received data from (‘127.0.0.1’, 8085):b’OK!’CPUUsed:1.20%received data from (‘127.0.0.1’, 8085):b’OK!’MemTotal:20880432.00