Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

file 77 lines (71 sloc) 2.805 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
'UDP request-response client.'
# Uses blocking sockets - however, I can't think of a situation where running
# several of these in parallel would be useful. (This is meant to be used
# primarily by nodes, not the master server.)
#
# I guess pinging multiple nodes simultaneously could be neat... But with since
# the nodes send updates to the master at most every ~30 minutes, why hurry?
#
# Besides, I'd have to make ping/SSH checks non-blocking too, which would raise
# the question of why I was re-writing the Twisted framework from the ground up?
#
# If the peers were linked in some kind of P2P network... All the layouts I
# considered had neighbourhoods far too small to be worth making non-blocking,
# though. Especially considering how cheap Python threads are.
#
# When I get around to passing tasks from the master server, I'll probably make
# that non-blocking.
import socket
import net
import traceback
import struct

BUFFSIZE=4096

def connect(hostname):
    'Returns a socket on success, raises a socket.error on failure.'
    #try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(net.timeout)
    s.connect((hostname,net.port))
    return s
    #except:
        #print 'Failed to connect to',hostname
        #traceback.print_exc()
        #raise socket.error('connect failed')

def ask(sock, request):
    'Returns a response on success, raises a socket.error on failure.'
    # prefix with length for TCP transit
    request = struct.pack('!H', len(request)+2) + request
    # send request
    sent = sock.send(request)
    while sent<len(request):
        request=request[sent:]
        sent = sock.send(request)
    # receive response according to protocol definition in server.py
    acc = ''
    while 1:
        chunk = sock.recv(BUFFSIZE)
        if not chunk:
            # check for 0-bytes-received disconnect
            raise socket.error('response not received when asked')
        acc += chunk
        # NOTE: this function would be buggy if sending+receiving ever
        # was asynchronous, due to accumulator losses on over-receives
        # Luckily, a single-threaded request-response model prevents
        # that from happening.

        # check if an entire response has been accumulated
        if len(acc)>2:
            size = struct.unpack('!H',acc[:2])[0]
            # pop it off and return it, if so
            if len(acc)>=size:
                response,acc = acc[:size],acc[size:]
                return response[2:]

def close(sock):
    'Probably optional, what with garbage collection and whatnot.'
    # useful in some situations though
    sock.shutdown(socket.SHUT_RDWR)
    sock.close()

if __name__=='__main__':
    import protocol.node
    s=connect('127.0.1.1')
    print ask(s, protocol.node.ping())
    close(s)
Something went wrong with that request. Please try again.