概述

Unlike TCP, UDP has no notion of connections. A UDP socket can receive datagrams from any server on the network and send datagrams to any host on the network. In addition, datagrams may arrive in any order, never arrive at all, or be duplicated in transit.

Since there are no connections, we only use a single object, a protocol, for each UDP socket. We then use the reactor to connect this protocol to a UDP transport, using the twisted.internet.interfaces.IReactorUDP reactor API.

和TCP不同,UDP没有连接。一个UDP套接字可以接受来自网络上的任何一台机器的报文,也可以发报文给任何一台主机。此外,报文可以以任意顺序到达,或者根本不到达(丢失),或者传输过程中发生重复。

既然没有连接,对于每一个UDP套机字,我们只需要使用一个协议的对象与之对应即可。然后可以用reactor来将协议对象绑定到UDP传输器(transport)上,使用 twisted.internet.interfaces.IReactorUDP 里面实现的reactor API。

DatagramProtocol

The class where you actually implement the protocol parsing and handling will usually be descended from twisted.internet.protocol.DatagramProtocol or from one of its convenience children. The DatagramProtocol class receives datagrams and can send them out over the network. Received datagrams include the address they were sent from. When sending datagrams the destination address must be specified.

Here is a simple example:

我们的协议类,需要继承 twisted.internet.protocol.DatagramProtocol 类或者其子类。DatagramProtocol 类可以在网络上接受和发送报文。报文包括其发送地址。发送报文时必须指定接收地址。

下面是个简单的例子:

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor class Echo(DatagramProtocol): def datagramReceived(self, data, (host, port)):
print "received %r from %s:%d" % (data, host, port)
self.transport.write(data, (host, port)) reactor.listenUDP(9999, Echo())
reactor.run()

As you can see, the protocol is registered with the reactor. This means it may be persisted if it’s added to an application, and thus it has startProtocol and stopProtocol methods that will get called when the protocol is connected and disconnected from a UDP socket.

如你所见,协议跟reactor一起注册。这意味着如果在程序中使用协议,它可以持久化,因而有一个startProtocolstopProtocol 方法,分别在协议与套接字连接上和断开时调用。

The protocol’s transport attribute will implement the twisted.internet.interfaces.IUDPTransport interface. Notice that the host argument should be an IP address, not a hostname. If you only have the hostname use reactor.resolve() to resolve the address (seetwisted.internet.interfaces.IReactorCore.resolve).

协议的transport属性实现了twisted.internet.interfaces.IUDPTransport接口。注意host参数必需是一个ip地址,不是主机名。如果只有主机名,可用reactor.resolve()得到地址(参考wisted.internet.interfaces.IReactorCore.resolve).

Adopted Datagram Ports

端口复用(提供基于socket的端口的来决定监听的端口的灵活性)

It is also possible to add an existing SOCK_DGRAM file descriptor to the reactor using the adoptDatagramPort API.

Here is a simple example:

可以将一个已经存在的SOCK_DRAM文件描述符添加到reactor,通过使用adoptDatagramPort API.

下面是一个简单的例子:

import socket

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor class Echo(DatagramProtocol):
def datagramReceived(self, data, (host, port)):
print "received %r from %s:%d" % (data, host, port)
self.transport.write(data, (host, port)) portSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Make the port non-blocking and start it listening.确保端口是非阻塞的,然后才开始绑定
portSocket.setblocking(False)
portSocket.bind(('127.0.0.1', 9999)) # Now pass the port file descriptor to the reactor
port = reactor.adoptDatagramPort(
portSocket.fileno(), socket.AF_INET, Echo()) # The portSocket should be cleaned up by the process that creates it.
portSocket.close() reactor.run()

  

Note

  • You must ensure that the socket is non-blocking before passing its file descriptor to adoptDatagramPort.
  • adoptDatagramPort cannot (currently) detect the family of the adopted socket so you must ensure that you pass the correct socket family argument.
  • The reactor will not shutdown the socket. It is the responsibility of the process that created the socket to shutdown and clean up the socket when it is no longer needed

注意:

  • 必须确保套接字是非阻塞的,才能将它的文件描述符传给adoptDatagramPort.
  • adoptDatagramPort(目前)不能检测采取的套接字的家族(family),因此务必确保falimy参数的正确赋值
  • reactor不会关闭套接字。创建套接字的进程应当在不使用之的情况下关闭然后清除套接字。

Connected UDP

"连接型"的UDP

A connected UDP socket is slightly different from a standard one as it can only send and receive datagrams to/from a single address. However this does not in any way imply a connection as datagrams may still arrive in any order and the port on the other side may have no one listening. The benefit of the connected UDP socket is that it may provide notification of undelivered packages. This depends on many factors (almost all of which are out of the control of the application) but still presents certain benefits which occasionally make it useful.

Unlike a regular UDP protocol, we do not need to specify where to send datagrams and are not told where they came from since they can only come from the address to which the socket is ‘connected’.

"连接型"的UDP套接字和常规的套接字有点不同,它只能发送报文到固定地址,或者从该固定地址接受报文。这不是说报文基于连接,因为报文还是可能以任意的顺序抵达,且有可能端口的另外一端没有任何套接字监听。”连接型“的UDP套接字的好处是,它可以提供通知---哪些包没有被传输。这取决于很多因素(知乎所有的因素都超出程序的控制),但仍然还是在某些情况下会比较有用。

和常规的UDP协议不同,不需要指定目标地址,也不需要知道源地址,因为他们都来自套接字”被绑定“的地址。

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor class Helloer(DatagramProtocol): def startProtocol(self):
host = "192.168.1.1"
port = 1234 self.transport.connect(host, port)
print "now we can only send to host %s port %d" % (host, port)
self.transport.write("hello") # no need for address,不需要地址 def datagramReceived(self, data, (host, port)):
print "received %r from %s:%d" % (data, host, port) # Possibly invoked if there is no server listening on the
# address to which we are sending.如果发送到的地址,没有人监听,可能会调用以下函数。
def connectionRefused(self):
print "No one listening" # 0 means any port, we don't care in this case; 0表示任何端口,这种情形下我们
reactor.listenUDP(0, Helloer())
reactor.run()

Note that connect(), like write() will only accept IP addresses, not unresolved hostnames. To obtain the IP of a hostname use reactor.resolve() , e.g.:

注意到connect(),和write一样,只能接受ip地址,而不是主机名。通过主机名得到ip地址要使用reactor.resolve(),如下:

from twisted.internet import reactor

def gotIP(ip):
print "IP of 'example.com' is", ip
reactor.callLater(3, reactor.stop) reactor.resolve('example.com').addCallback(gotIP)
reactor.run()

Connecting to a new address after a previous connection or making a connected port unconnected are not currently supported, but likely will be in the future.

”连接“到一个新的地址,在之前已经有”连接“的情况下,或者使得”连接“的端口变成”非连接“,这些在目前都是做不到的,将来也许可以。

Multicast UDP

组播UDP

Multicast allows a process to contact multiple hosts with a single packet, without knowing the specific IP address of any of the hosts. This is in contrast to normal, or unicast, UDP, where each datagram has a single IP as its destination. Multicast datagrams are sent to special multicast group addresses (in the IPv4 range 224.0.0.0 to 239.255.255.255), along with a corresponding port. In order to receive multicast datagrams, you must join that specific group address. However, any UDP socket can send to multicast addresses.

组播允许协议使用一个包联系多个主机,不需要知道这些主机的具体ip地址。这个常规的,单路通讯的udp不同,常规的单路通讯的udp的每个报文都有一个唯一的ip地址,作为它的目的地址。

组播的报文被送到一些特殊的组播地址(ipv4里面从224.0.0.0到239.255.255.255),附有一个相应的端口。 要接受组播报文,你必须加入到那个指定的组地址里面去。

然后,任何udp套接字都可以发送到组播地址。

MulticastServer.py

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor class MulticastPingPong(DatagramProtocol): def startProtocol(self):
"""
Called after protocol has started listening.
"""
# Set the TTL>1 so multicast will cross router hops:设置TTL>1,以便跳过路由器,TTL定义最多跳几次
self.transport.setTTL(5)
# Join a specific multicast group:加入到一个具体的组播组
self.transport.joinGroup("228.0.0.5") def datagramReceived(self, datagram, address):
print "Datagram %s received from %s" % (repr(datagram), repr(address))
if datagram == "Client: Ping":
# Rather than replying to the group multicast address, we send the
# reply directly (unicast) to the originating port:
self.transport.write("Server: Pong", address) # We use listenMultiple=True so that we can run MulticastServer.py and
# MulticastClient.py on same machine: 使listenMultiple=True才能在同一台机器上运行客户端和服务器。
reactor.listenMulticast(8005, MulticastPingPong(),
listenMultiple=True)
reactor.run()

As with UDP, with multicast there is no server/client differentiation at the protocol level. Our server example is very simple and closely resembles a normal listenUDP protocol implementation. The main difference is that instead of listenUDPlistenMulticast is called with the port number. The server calls joinGroup to join a multicast group. A DatagramProtocol that is listening with multicast and has joined a group can receive multicast datagrams, but also unicast datagrams sent directly to its address. The server in the example above sends such a unicast message in reply to the multicast message it receives from the client.

使用udp,组播,客户端和服务器端在协议级别是没有差异的。上述服务器端代码比较简单,极似一个常规的 listenUDP协议的实现。主要区别在于,listenMulticast的调用中有端口。服务器端调用joinGroup加入到一个组播组。一个DatagramProtocol实例监听组播,加入到了可以接受组播报文的组中,但也可以接受直接发送给它的单路报文。上面的例子就发送了这样的一个单路广播的消息给它接受的消息的发送者。

MulticastClient.py

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor class MulticastPingClient(DatagramProtocol): def startProtocol(self):
# Join the multicast address, so we can receive replies:
self.transport.joinGroup("228.0.0.5")
# Send to 228.0.0.5:8005 - all listeners on the multicast address
# (including us) will receive this message.
self.transport.write('Client: Ping', ("228.0.0.5", 8005)) def datagramReceived(self, datagram, address):
print "Datagram %s received from %s" % (repr(datagram), repr(address)) reactor.listenMulticast(8005, MulticastPingClient(), listenMultiple=True)
reactor.run()

  

Note that a multicast socket will have a default TTL (time to live) of 1. That is, datagrams won’t traverse more than one router hop, unless a higher TTL is set with setTTL. Other functionality provided by the multicast transport includes setOutgoingInterface andsetLoopbackMode – see IMulticastTransport for more information.

注意到一个组播的套接字默认的TTL为1。 就是说,报文不会跨任何一个路由器转发,除非通过setTTL设置了一个更大值。组播的传输器(transport)还有其他的函数比如setOutgoingInterface,以及setLoopbackMode - 具体请参考IMulticastTransport

Broadcast UDP

广播式UDP

Broadcast allows a different way of contacting several unknown hosts. Broadcasting via UDP sends a packet out to all hosts on the local network by sending to a magic broadcast address ("<broadcast>"). This broadcast is filtered by routers by default, and there are no “groups” like multicast, only different ports.

Broadcast is enabled by passing True to setBroadcastAllowed on the port. Checking the broadcast status can be done with getBroadcastAllowed on the port.

For a complete example of this feature, see udpbroadcast.py.

广播,提供了另外一种方式来联系一些未知的主机。通过UDP进行广播,将会把包发送给网络上的所有主机,只需要发送给一个神奇的广播地址("<broadcast>")。这个广播默认会被路由器过滤,没有类似于多路广播中的”组“的东东,只有不同的端口。

广播,只需要在端口上指定setBroadcastAllowed为True即可。检查端口上广播的状态可以通过getBroadcastAllowed来实现。

要知道完整的特性,请参考这里的例子 udpbroadcast.py.

twisted udp编程的更多相关文章

  1. Twisted UDP编程技术

    实战演练1:普通UDP UDP是一种无连接对等通信协议,没有服务器和客户端概念,通信的任何一方均可通过通信原语直接和其他方通信 1.相对于TCP,UDP编程只需定义DatagramProtocol子类 ...

  2. Linux学习四:UDP编程(上)

    关于UDP和TCP对比优缺,这里就不说了. 使用UDP代码所掉用的函数和用于TCP的函数非常类似,这主要因为套接口库在底层的TCP和UDP的函数上加了一层抽象,通过这层抽象使得编程更容易,但失去了一些 ...

  3. [C# 网络编程系列]专题七:UDP编程补充——UDP广播程序的实现

    转自:http://www.cnblogs.com/zhili/archive/2012/09/03/2666974.html 上次因为时间的关系,所以把上一个专题遗留下的一个问题在本专题中和大家分享 ...

  4. [C# 网络编程系列]专题六:UDP编程

    转自:http://www.cnblogs.com/zhili/archive/2012/09/01/2659167.html 引用: 前一个专题简单介绍了TCP编程的一些知识,UDP与TCP地位相当 ...

  5. Socket编程实践(12) --UDP编程基础

    UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...

  6. 【Socket编程】通过Socket实现UDP编程

    通过Socket实现UDP编程 UDP通信: 1.UDP协议(用户数据报协议)是无连接.不可靠.无序的. 2.UDP协议以数据报作为数据传输的载体. 3.使用UDP进行数据传输时,首先需要将要传输的数 ...

  7. 网络编程之UDP编程

    网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了Data ...

  8. 五十六、linux 编程——UDP 编程模型

    56.1 UDP 编程模型 56.1.1 编程模型 UDP 协议称为用户数据报文协议,可靠性比 TCP 低,但执行效率高 56.1.2 API (1)发送数据 函数参数: sockfs:套接字文件描述 ...

  9. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

随机推荐

  1. Linq之IQueryable接口与IEnumberable区别

    IEnumerable接口 公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代.也就是说:实现了此接口的object,就可以直接使用foreach遍历此object: IEnumerable 包含 ...

  2. DOM系列---DOM获取尺寸和位置

    内容提纲: 1.获取元素CSS大小 2.获取元素实际大小 3.获取元素周边大小 本篇我们主要讨论一下页面中的某一个元素它的各种大小和各种位置的计算方式. 一.获取元素CSS大小 1.通过style获取 ...

  3. [设计模式]第四回:建造者模式(Builder Pattern)

    1.概述 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,这就是建造者模式. 简单的说就是生产一个产品的步骤比较稳定,单个步骤变化会产生一个不同的产品. 2.实践 物理模型 建造 ...

  4. Daily Scrum – 1/4

    Meeting Minutes 大家讨论了一下作业的内容,以及用户的反馈,商量了一下长期计划(naive)的完成方式. 好像有些时候用户测试的时候会崩溃,不过我们自己用的时候一直没有出现过,分析可能是 ...

  5. 软工实践个人练习-使用github进行代码管理

    1.掌握使用Git进行代码版本,使用github进行代码托管. 2.创建小组Organization,并邀请组员进来. 3.将代码库https://github.com/sefzu2015/AutoC ...

  6. easyUI API(version 1.5)

    不分先后,只做记录. jquery+easyui培训文档下载地址: 链接: https://pan.baidu.com/s/1dFgFXk9 密码: jj5d 1 easyui-draggable(拖 ...

  7. abstract和接口

    接口只包含常量和抽象方法,不能实例化. abstract: 1.抽象类不能实例化, 2.可以没有抽象方法.但有了抽象方法,一定要被定义为抽象类. 3.子类没有实现父类中所有的抽象方法.子类也必须定义为 ...

  8. OC基础--OC中类的定义

    OC中类的定义与使用跟C#和java差别相当明显,做个笔记,牢记并加以区别! 一.OC中类的定义:关键字@implementation 和 @end 注意事项: 1.定义好一个类之后,要让这个类继承N ...

  9. vijos p1523 贪吃的九头龙 思考思考再思考,就荒废了4小时

    树形DP要有自己的风格,转二叉树是基础,考虑边界最头疼. #include<cstdio> #include<cstring> #include<algorithm> ...

  10. 用php生成数据字典

    <?php header("Content-type: text/html; charset=utf-8"); $dbserver = "localhost&quo ...