[Kamaelia] TCPClient: How to sense connection failure?

Hendrik van Rooyen mail at microcorp.co.za
Sat Jan 12 03:44:39 EST 2008


"Bjoern Schliessmann" <usenet-mail,,...m> wrote:

>I'm currently trying to implement a simulation program with Kamaelia
>and need a reliable TCP connection to a data server.
>
>From Twisted, I know that a method is called if the connection fails
>by whatever reason. I tried to get the same results with Kamaelia's
>TCPClient component. If I start up the component and try to connect
>to a closed TCP port it fails and sends a message out of the signal
>box, that's okay.
>
>But if the connection attempt succeeds and, after some time, the
>server drops the connection with full TCP handshake (FIN, FIN+ACK,
>ACK), the component just hangs and does nothing. Is this by design,
>or could there be an error in my setup?
>

Not sure about Kamelia, but I have found that when a FIN comes along,
a socket.recv() gives back an empty string, just like EOF on a file.

I always have my sockets unblocked and fitted with time outs, but then
I am basically a broken down assembler programmer, so there are probably
better techniques around.

Below is what I use - a sort of netstring, synced on a tilde, with human
readable length implementation and escaping of tildes and the escape character.

It seems to work reliably for me, and detects when the server goes down.

The code for a typical client is below.  If anybody is interested I will post
the server
too, but it should be trivial to make, given the example below.

I hope the tabs survive the journey

- Hendrik

#start of code fragment

def sockget_len(s,L,data):
 """
 This fills a buffer of given length from the socket s, recursively.

 s   is the socket
 L   is the length to receive
 data  is the buffer
 """

 error = 0
 req_L = L - len(data)
 try:
  data = data+s.recv(req_L)
 except socket.error,msg:  # broken pipes again
  if 'timed out' in msg:
   rec = '2'*L
   return 2,rec   # time out
  print 'socket error while receiving',msg
  rec = '1'*L
  return 1,rec    # error = 1 is a snafu
 if not data:
  print 'end of file while receiving'
  rec = '0'*L
  return 3,rec    # This is end of file
 if len(data) != L:
  error,data = sockget_len(s,L,data)
 return error,data


def sockget(s):
 """
 Gets a transmission from host.
 """

 while True:
  tilde = ''
  error,tilde = sockget_len(s,1,tilde) # sync up on tilde
  if error == 1:
   return error,''
  elif error == 2:
   return error,''
  elif error == 3:
   return error,''
  if tilde == '~':
   break

 length = ''
 error,length = sockget_len(s,4,length) # get the length of the data
 if error == 1:
  return error,''   # real error
 elif error == 2:
  return error,''   # Time out
 elif error == 3:
  return error,''   # End of file
 L = int(length)
 buf = ''
 error,data = sockget_len(s,L,buf)  # get the data of length L
 return error, data    # same errors as above 0 is all right


#  client communications program

def comms_thread(qi,qo):
 """This listens for the latest values, and sends requests up."""

 while True:

  HOST = 'Linuxbox'   # The remote host
  PORT = 50007        # The same port as used by the server
  socket.setdefaulttimeout(10.00)
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

  while True:
   try:
    qi.put('Connecting,')
    s.connect((HOST, PORT))
    break
   except socket.error,msg:
    print 'error msg is:',msg
    time.sleep(10)
    continue
  print 'Connected - Time out is:',s.gettimeout()
  qi.put('Connected,')

  last_rx_time = time.time()
  last_tx_time = time.time()
  while True:

   while True:
    error,data = sockget(s)  # see if a message from host
    if error == 0 and data:
     msg2 = data.replace('/\x81','~')
     msg1 = msg2.replace('/\xd0','/')
     qi.put(msg1)
     print 'received',msg1
     last_rx_time = time.time()
     break
    elif  error == 1:
     print 'Error after sockget'
     break
    if time.time() - last_rx_time > 180:
     print 'no comms from host for 3 minutes'
     error = 1
     break   # time out ok, unless they are too long
    if error == 2:
     error = 0  # time outs are all right here
     break
    if error == 3:
     error = 1
     break   # end of files are a snafu

   if error == 1:
    break

   try:
    i_string = qo.get(block=False) # see if stuff to transmit
   except Queue.Empty:
    if time.time()-last_tx_time > 8.5:
     i_string = 'Keepalive'  # if not for a while, tell server we are alive
     print 'sending keepalive'
    else:
     time.sleep(0.1)    # else wait a while and carry on
     continue

   msg1 = i_string.replace('/','/\xd0')
   msg2 = msg1.replace('~','/\x81')
   length = str(len(msg2))
   L = len(length)
   if L == 1:
    length = '000'+length
   elif L == 2:
    length = '00'+length
   elif L == 3:
    length = '0'+length
   try:
    s.send('~'+length+msg2)
    last_tx_time = time.time()
   except socket.error,msg:
    print 'Socket error on transmit',msg
    break
   time.sleep(0.1)

  s.close()  # Formally close the broken thing
  qi.put('Quit,') # Tell main thread its hopeless

 sys.exit()  # Clobber this thread







More information about the Python-list mailing list