Sophie

Sophie

distrib > Mandriva > 2008.1 > x86_64 > media > main-release > by-pkgid > db93d7191b12a3d5ce887da46fc79bf1 > files > 261

python-twisted-core-doc-2.5.0-3mdv2008.1.x86_64.rpm

<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: UDP Networking</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">UDP Networking</h1><div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">DatagramProtocol</a></li><li><a href="#auto2">Connected UDP</a></li><li><a href="#auto3">Multicast UDP</a></li><li><a href="#auto4">Acknowledgements</a></li></ol></div><div class="content"><span></span><h2>Overview<a name="auto0"></a></h2><p>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.</p><p>Since there are no multiple 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 
    <code class="API">twisted.internet.interfaces.IReactorUDP</code>
    reactor API.</p><h2>DatagramProtocol<a name="auto1"></a></h2><p>At the base, the place where you actually implement the protocol
    parsing and handling, is the DatagramProtocol class. This class will
    usually be decended from <code class="API">twisted.internet.protocol.DatagramProtocol</code>. Most
    protocol handlers inherit either from this class 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, and when sending datagrams the address to
    send to must be specified.</p><p>Here is a simple example:</p><pre class="python">
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">DatagramProtocol</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">DatagramProtocol</span>):
    
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">datagramReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>, (<span class="py-src-parameter">host</span>, <span class="py-src-parameter">port</span>)):
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;received %r from %s:%d&quot;</span> % (<span class="py-src-variable">data</span>, <span class="py-src-variable">host</span>, <span class="py-src-variable">port</span>)
        <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>, (<span class="py-src-variable">host</span>, <span class="py-src-variable">port</span>))

<span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenUDP</span>(<span class="py-src-number">9999</span>, <span class="py-src-variable">Echo</span>())
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
</pre><p>As you can see, the protocol is registed with the reactor. This means
    it may be persisted if it's added to an application, and thus it has
    <code class="API">twisted.internet.protocol.DatagramProtocol.startProtocol</code>
    and <code class="API">twisted.internet.protocol.DatagramProtocol.stopProtocol</code>
    methods that will get called when the protocol is connected and
    disconnected from a UDP socket.</p><p>The protocol's <code class="python">transport</code> attribute will
    implement the <code class="API">twisted.internet.interfaces.IUDPTransport</code> interface.
    Notice that the <code class="python">host</code> argument should be an
    IP, not a hostname. If you only have the hostname use <code class="python">reactor.resolve()</code> to resolve the address (see <code class="API">twisted.internet.interfaces.IReactorCore.resolve</code>).</p><h2>Connected UDP<a name="auto2"></a></h2><p>A connected UDP socket is slighly different from a standard one - it
    can only send and receive datagrams to/from a single address, but this
    does not in any way imply a connection. 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 it <strong>may</strong>
    provide notification of undelivered packages. This depends on many
    factors, almost all of which are out of the control of the application,
    but it still presents certain benefits which occassionally make it
    useful.</p><p>Unlike a regular UDP protocol, we do not need to specify where to
    send datagrams to, and are not told where they came from since
    they can only come from address the socket is 'connected' to.</p><pre class="python">
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">DatagramProtocol</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">Helloer</span>(<span class="py-src-parameter">DatagramProtocol</span>):

    <span class="py-src-keyword">def</span> <span class="py-src-identifier">startProtocol</span>(<span class="py-src-parameter">self</span>):
        <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">connect</span>(<span class="py-src-string">&quot;192.168.1.1&quot;</span>, <span class="py-src-number">1234</span>)
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;we can only send to %s now&quot;</span> % <span class="py-src-variable">str</span>((<span class="py-src-variable">host</span>, <span class="py-src-variable">port</span>))
        <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">&quot;hello&quot;</span>) <span class="py-src-comment"># no need for address</span>
        
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">datagramReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>, (<span class="py-src-parameter">host</span>, <span class="py-src-parameter">port</span>)):
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;received %r from %s:%d&quot;</span> % (<span class="py-src-variable">data</span>, <span class="py-src-variable">host</span>, <span class="py-src-variable">port</span>)

    <span class="py-src-comment"># Possibly invoked if there is no server listening on the
</span>    <span class="py-src-comment"># address to which we are sending.
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionRefused</span>(<span class="py-src-parameter">self</span>):
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;Noone listening&quot;</span>

<span class="py-src-comment"># 0 means any port, we don't care in this case
</span><span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenUDP</span>(<span class="py-src-number">0</span>, <span class="py-src-variable">Helloer</span>())
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
</pre><p>Note that <code class="python">connect()</code>, like <code class="python">write()</code> will only accept IP addresses, not
    unresolved domain names. To obtain the IP of a domain name use <code class="python">reactor.resolve()</code>, e.g.:</p><pre class="python">
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>

<span class="py-src-keyword">def</span> <span class="py-src-identifier">gotIP</span>(<span class="py-src-parameter">ip</span>):
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;IP of 'example.com' is&quot;</span>, <span class="py-src-variable">ip</span>

<span class="py-src-variable">reactor</span>.<span class="py-src-variable">resolve</span>(<span class="py-src-string">'example.com'</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">gotIP</span>)
</pre><p>Connecting to a new address after a previous connection, or
    making a connected port unconnected are not currently supported,
    but will likely be supported in the future.</p><h2>Multicast UDP<a name="auto3"></a></h2><p>A multicast UDP socket can send and receive datagrams from multiple clients.
    The interesting and useful feature of the multicast is that a client can
    contact multiple servers with a single packet, without knowing the specific IP
    of any of the hosts.</p><div class="py-listing"><pre>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">DatagramProtocol</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">MulticastServer</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">MulticastServerUDP</span>(<span class="py-src-parameter">DatagramProtocol</span>):
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">startProtocol</span>(<span class="py-src-parameter">self</span>):
        <span class="py-src-keyword">print</span> <span class="py-src-string">'Started Listening'</span>
        <span class="py-src-comment"># Join a specific multicast group, which is the IP we will respond to
</span>        <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">joinGroup</span>(<span class="py-src-string">'224.0.0.1'</span>)

    <span class="py-src-keyword">def</span> <span class="py-src-identifier">datagramReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">datagram</span>, <span class="py-src-parameter">address</span>):
        <span class="py-src-comment"># The uniqueID check is to ensure we only service requests from
</span>        <span class="py-src-comment"># ourselves
</span>        <span class="py-src-keyword">if</span> <span class="py-src-variable">datagram</span> == <span class="py-src-string">'UniqueID'</span>:
            <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;Server Received:&quot;</span> + <span class="py-src-variable">repr</span>(<span class="py-src-variable">datagram</span>)
            <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">&quot;data&quot;</span>, <span class="py-src-variable">address</span>)

<span class="py-src-comment"># Note that the join function is picky about having a unique object
</span><span class="py-src-comment"># on which to call join.  To avoid using startProtocol, the following is
</span><span class="py-src-comment"># sufficient:
</span><span class="py-src-comment">#reactor.listenMulticast(8005, MulticastServerUDP()).join('224.0.0.1')
</span>
<span class="py-src-comment"># Listen for multicast on 224.0.0.1:8005
</span><span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenMulticast</span>(<span class="py-src-number">8005</span>, <span class="py-src-variable">MulticastServerUDP</span>())
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
</pre><div class="caption">Source listing - <a href="listings/udp/MulticastServer.py"><span class="filename">listings/udp/MulticastServer.py</span></a></div></div><p>
    The server protocol is very simple, and closely resembles a normal listenUDP
    implementation.  The main difference is that instead of listenUDP,
    listenMulticast is called with a specified port number.  The server must also
    call joinGroup to specify on which multicast IP address it will service
    requests.  Another item of interest is the contents of the datagram.  Many
    different applications use multicast as a way of device discovery, which leads
    to an abundance of packets flying around.  Checking the payload can ensure
    that we only service requests from our specific clients.
    </p><div class="py-listing"><pre>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">DatagramProtocol</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">MulticastServer</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">MulticastClientUDP</span>(<span class="py-src-parameter">DatagramProtocol</span>):

    <span class="py-src-keyword">def</span> <span class="py-src-identifier">datagramReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">datagram</span>, <span class="py-src-parameter">address</span>):
            <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;Received:&quot;</span> + <span class="py-src-variable">repr</span>(<span class="py-src-variable">datagram</span>)

<span class="py-src-comment"># Send multicast on 224.0.0.1:8005, on our dynamically allocated port
</span><span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenUDP</span>(<span class="py-src-number">0</span>, <span class="py-src-variable">MulticastClientUDP</span>()).<span class="py-src-variable">write</span>(<span class="py-src-string">'UniqueID'</span>,
                                                 (<span class="py-src-string">'224.0.0.1'</span>, <span class="py-src-number">8005</span>))
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
</pre><div class="caption">MulticastServer.py - <a href="listings/udp/MulticastClient.py"><span class="filename">listings/udp/MulticastClient.py</span></a></div></div><p>
    This is a mirror implementation of a standard UDP client.  The only difference
    is that the destination IP is the multicast address.  This datagram will be
    distributed to every server listening on 224.0.0.1 and port 8005.  Note that
    the client port is specified as 0, as we have no need to keep track of what
    port the client is listening on.
    </p><h2>Acknowledgements<a name="auto4"></a></h2><p>Thank you to all contributors to this document, including:</p><ul><li>Kyle Robertson, author of the explanation and examples of multicast</li></ul></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 2.5.0</span></body></html>