twistedtwisted.applicationtwistd

Run Non-Twisted-based Python script daemonized with twistd


I'm writing a Python program consisting of a server (using Twisted) and a client (without Twisted)

The server part is implemented using Twisted and Twisted's application framework and launched with Twistd to be daemonized.

The client which runs on a different server is a simple Python script without any Twisted stuff (and no application framework specific stuff). It should also be run as a Daemon. FYI, this is the source:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import sys
import time
import syslog

SERVER_IP = '127.0.0.1' 
SERVER_PORT = 43278 
BEAT_PERIOD = 1

class HeartbeatClient:
    '''
    A Client sending heartbeats to a monitoring server.
    '''
    def __init__(self, server_ip, port, beat_period):
        syslog.syslog( ('Sending heartbeat to IP %s , port %d' +
                        '\n press Ctrl-C to stop\n') 
                        % (SERVER_IP, SERVER_PORT))

    def run(self):
        while True:
            hbSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            hbSocket.sendto('PyHB', (SERVER_IP, SERVER_PORT))
            if __debug__: 
                print 'Time: %s' % time.ctime()
            time.sleep(BEAT_PERIOD)

if __name__ == '__main__':
    hbc = HeartbeatClient() 
    hbc.run()

Now I wonder if I can daemonize the client also with Twistd? Therefore I would have create an Twisted-Application out of the client. But all examples I saw concerning Twisted applications where implementing some Twisted internet-server stuff (like in my case internet.UDPServer...), which my client does not use.

So is it possible to use Twistd to launch my client as a daemon, and what changes do I have to make? Should I rewrite the client to take full use of Twisted? If yes, are there any similar examples out there how to write a Twisted based network client?

Or do I have to use a different daemonize library for the client? There is a good library for that, but I'm trying to be consistent and use the same daemonizing mechanism for client and server.


Solution

  • With Twisted, as a tac file, your HeartbeatClient would look something like this:

    from twisted.application.service import Application, Service
    from twisted.internet import reactor
    from twisted.internet.task import LoopingCall
    from twisted.internet.protocol import DatagramProtocol
    
    class HeartbeatClient(Service):
        def startService(self):
            self._call = LoopingCall(self._heartbeat)
            self._call.start(BEAT_PERIOD)
    
        def stopService(self):
            self._call.stop()
    
        def _heartbeat(self):
            port = reactor.listenUDP(0, DatagramProtocol())
            port.write('PyHB', (SERVER_IP, SERVER_PORT))
            port.stopListening()
    
    application = Application("PyHB")
    HeartbeatClient().setServiceParent(application)
    

    Note the use of reactor.listenUDP, even though you're only sending UDP datagrams, not receiving any. UDP doesn't really have the concept of clients and servers, it only has open ports. All UDP ports can send and receive datagrams. That's why there's only reactor.listenUDP, not reactor.connectUDP.

    Aside from that, LoopingCall gives you the loop you want, and putting the code into a custom Service subclass lets you start and stop the loop at the appropriate times.