27

I'm writing an app in Python 2.7.5 using the raw socket interface (import socket).

I need to know if there's a way I can either register some sort of event, or easily test at a regular interval, if a TCP socket connection is still connected and established.

Most of the advice I've found says things like "just try to read from it; if it's closed you'll get an error or no bytes." However, sometimes I'm not ready to read from it but I still want to know if the socket gets closed, so for example I can manually reconnect immediately.

Also, reading from the socket would remove the bytes read from the buffer, so reading simply to test for connection "liveness" doesn't seem feasible.

Having a background thread to check for connection liveness would be OK with me. That thread could check say once per second to see if the connection is live; if it is dropped, then it could call a function, or set a variable or some other thing so that the app knows the connection closed. Knowing WHY it closed (reset by peer, timeout, etc) would be even more useful....

Any advice on this?

fdmillion
  • 4,823
  • 7
  • 45
  • 82
  • on Posix; use `select()` (or `epoll()` for Linux) – tMC Jun 30 '13 at 03:26
  • Related: http://stackoverflow.com/questions/17705239/is-there-a-way-to-detect-that-tcp-socket-has-been-closed-by-the-remote-peer-wit (TL;DR: there's no good way to find out if the connection has been closed unless you are willing to read all of the bytes coming from the socket first; TCP sockets are implemented that way intentionally because they are based on the idea of FIFO event ordering, i.e. they want you to read the incoming data in order, and only find out about the end-of-stream after you have read it all) – Jeremy Friesner Jan 07 '17 at 16:16

1 Answers1

33

Use select like this tutorial suggested http://docs.python.org/3/howto/sockets.html#non-blocking-sockets

If a socket is in the output readable list, you can be as-close-to-certain-as-we-ever-get-in-this-business that a recv on that socket will return something. Same idea for the writable list. You’ll be able to send something. Maybe not all you want to, but something is better than nothing. (Actually, any reasonably healthy socket will return as writable - it just means outbound network buffer space is available.)

... if somewhere in those input lists of sockets is one which has died a nasty death, the select will fail.

(The last paragraph is no longer there in the Python 3 version.)

This is the flow I usually use

import select
import socket

ip = '127.0.0.1'
port = 80

conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect((ip, port))

while True:
    try:
        ready_to_read, ready_to_write, in_error = \
            select.select([conn,], [conn,], [], 5)
    except select.error:
        conn.shutdown(2)    # 0 = done receiving, 1 = done sending, 2 = both
        conn.close()
        # connection error event here, maybe reconnect
        print('connection error')
        break
    if len(ready_to_read) > 0:
        recv = conn.recv(2048)
        # do stuff with received data
        print(f'received: {recv}')
    if len(ready_to_write) > 0:
        # connection established, send some stuff
        conn.send('some stuff')
tripleee
  • 175,061
  • 34
  • 275
  • 318
Steve Zhan
  • 553
  • 6
  • 9
  • Is there a way I can modify this for connection to Google sheets? I have a gspread connection and it drops after around 12 hours. i want to recognise the drop and reconnect. – Slarty Bartfast Nov 19 '22 at 06:47
  • when i connect such a socket to nc and then kill it or connect it to an http server, it will happily say ready to read even after the connection has been closed. it will even successfully read 0 bytes (where before it was just blocked). trying to write (it says ready to write) results in a "Broken Pipe" error. – FalcoGer Mar 30 '23 at 21:07