Twisted UDP编程技术

实战演练1:普通UDP

UDP是一种无连接对等通信协议,没有服务器和客户端概念,通信的任何一方均可通过通信原语直接和其他方通信

1、相对于TCP,UDP编程只需定义DatagramProtocol子类 无需定义Factory;完全基于twisted的方案

from twisted.internet.protocol import DatagramProtocol

from twisted.internet import reactor
import threading
import time
import datetime

class 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 = 8007

bStop = False
def 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 DatagramProtocol
from twisted.internet import reactor
import socket

class 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) # 适配普通socket

portSocket.close() # 在启动reactor之前关闭普通socket对象
reactor.run()

实战演练2:Connect UDP

 虽然UDP本身是无连接协议,但是编程接口仍然可以调用connect()函数,用来限制只与某地址和端口通信,当调用connect()函数,当需要向该地址发送数据时就不需要指定目标端口和地址了

Connect UDP本质上是数据报协议,虽然一定程度上实现了点对点链接

udp connected-udp tcp的比较
  UDP Connected UDP TCP
是否是点对点通信
数据包质检是否有序
发送是否可靠(发送方是否知晓数据已到达)
是否支持广播,组播

 

用Connected UDP改造后的UDP通信代码示例:

 

from twisted.internet.protocol import DatagramProtocol

from twisted.internet import reactor
import threading,time,datetime


host = '127.0.0.1'
port = 8007

class 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 = False

def 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 DatagramProtocol
from twisted.internet import reactor

multicast_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退出组播

 

posted @ 2017-06-27 16:15  Erick-LONG  阅读(988)  评论(0编辑  收藏  举报