How to Change Tcp Keepalive Timer Using Python Script

Issue using select() and TCP Keepalive with Python sockets

As the comments to your question have mentioned, the TCP_KEEPALIVE stuff won't make any difference for your use-case. TCP_KEEPALIVE is a mechanism for notifying a program when the peer on the other side of its TCP connection has gone away on an otherwise idle TCP connection. Since you are regularly sending data on the TCP connection(s), the TCP_KEEPALIVE functionality is never invoked (or needed) because the act of sending data over the connection is already enough, by itself, to cause the TCP stack to recognize ASAP when the remote client has gone away.

That said, I modified/simplified your example server code to get it to work (as correctly as possible) on my machine (a Mac, FWIW). What I did was:

  1. Moved the socket.setsockopt(SO_REUSEADDR) to before the bind() line, so that bind() won't fail after you kill and then restart the program.

  2. Changed the select() call to watch for writable-sockets.

  3. Added exception-handling around the send() calls.

  4. Moved the remove-socket-from-lists code into a separate RemoveSocketFromLists() function, to avoid redundant code

Note that the expected behavior for TCP is that if you quit a client gently (e.g. by control-C'ing it, or killing it via Task Manager, or otherwise causing it to exit in such a way that its host TCP stack is still able to communicate with the server to tell the server that the client is dead) then the server should recognize the dead client more or less immediately.

If, on the other hand, the client's network connectivity is disconnected suddenly (e.g. because someone yanked out the client computer's Ethernet or power cable) then it may take the server program several minutes to detect that the client has gone away, and that's expected behavior, since there's no way for the server to tell (in this situation) whether the client is dead or not. (i.e. it doesn't want to kill a viable TCP connection simply because a router dropped a few TCP packets, causing a temporary interruption in communications to a still-alive client)

If you want to try to drop the clients quickly in that scenario, you could try requiring the clients to send() a bit of dummy-data to the server every second or so. The server could keep track of the timestamp of when it last received any data from each client, and force-close any clients that it hasn't received any data from in "too long" (for whatever your idea of too long is). This would more or less work, although it risks false-positives (i.e. dropping clients that are still alive, just slow or suffering from packet-loss) if you set your timeout-threshold too low.

#!/usr/bin/env python3

import select, socket

# Listen Port
LISTEN_PORT = 1234

# Create socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Make socket reusable
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Setup the socket
server.setblocking(0)
server.bind(('0.0.0.0', LISTEN_PORT))
server.listen(5)

# Tell user we are listening
print("Listening on port %s" % LISTEN_PORT)

inputs = [server]
outputs = []

# Removes the specified socket from every list in the list-of-lists
def RemoveSocketFromLists(s, listOfLists):
for nextList in listOfLists:
if s in nextList:
nextList.remove(s)

while True:
# Detecting clients that disappeared does NOT work when we ARE
# watching if any sockets are writable
readable, writable, exceptional = select.select(inputs, outputs, [])

for s in readable:
if s is server:
connection, client_address = s.accept()

print("New client connected: %s" % (client_address,))
connection.setblocking(0)
inputs.append(connection)
outputs.append(connection)
else:
try:
data = s.recv(1024)
print("Data from %s: %s" % (s.getpeername(), data.decode('ascii').rstrip()))
except:
print("recv() reports that %s disconnected" % s)
RemoveSocketFromLists(s, [inputs, outputs, writable])

for s in writable:
try:
numBytesSent = s.send(b".")
except:
print("send() reports that %s disconnected" % s)
RemoveSocketFromLists(s, [inputs, outputs])

Python Sockets - Keeping a connection to a server alive from the client

For enabling keep alive there is a duplicate question at How to change tcp keepalive timer using python script?

Keep in mind some servers and intermediate proxies forcibly close long lived connections regardless of keep alives being used or not, in which case you will see a FIN,ACK after X amount of time no matter what.

robust continuous TCP connection (python socket)

I will try to answer both questions.

  1. ... Is there some way that the socket sends an interrupt for a connection that is dead ...

    I know none. TCP_KEEPALIVE only tries to maintain the connection. It is very useful if any equipment on the network flow has a timeout, because it prevents the timeout to abort the connection. But if the connection drops because because of any other reason (that timeout) TCP_KEEPALIVE cannot do anything. The rationale is that there is no need to restore a dropped inactive connection before something has to be exchanged.

  2. Is this a robust way of handling a continous TCP connection?

    Not really.

    The robust way is to be prepared that the connection fails for any reason at any moment. So you should be prepared to face an error when sending a message (your code is) and if that happens try to re-open the connection and send the message again (your current code does not). Something like:

    def connect(...):
    # establish and return a connection
    ...
    return clientSocket

    clientSocket = connect(...)
    while True:
    ...
    while True:
    try:
    clientSocket.send(message)
    break
    except OSError:
    clientSocket = connect()
    ...

Unrelated: your graceful shutdown is incorrect. The initiator (the part using shutdown) should not immediately close the socket, but start a read loop and only close when everything has be received and processed.

python requests keep connection alive for indefinite time

By using requests.Session, keep-alive is handled for you automatically.

In the first version of your loop that continuously polls the server after the /auth call is made, the server does not drop the connection due to the subsequent GET that happens. In the second version, it's likely that sleep interval exceeds the amount of time the server is configured to keep the connection open.

Depending on the server configuration of the API, the response headers may include a Keep-Alive header with information about how long connections are kept open at a minimum. HTTP/1.0 specifies this information is included in the timeout parameter of the Keep-Alive header. You could use this information to determine how long you have until the server will drop the connection.

In HTTP/1.1, persistent connections are used by default and the Keep-Alive header is not used unless the server explicitly implements it for backwards compatibility. Due to this difference, there isn't an immediate way for a client to determine the exact timeout for connections since it may exist solely as server side configuration.

The key to keeping the connection open would be to continue polling at regular intervals. The interval you use must be less than the server's configured connection timeout.

One other thing to point out is that artificially extending the length of the session indefinitely this way makes one more vulnerable to session fixation attacks. You may want to consider adding logic that occasionally reestablishes the session to minimize risk of these types of attacks.

Is detecting a TCP socket disconnect in Python possible?

It is perfectly fine for TCP connections to not transfer any kind of data for a long time. And it is also not a problem if a cable gets disconnected as long as it is reconnected later data need to be transmitted.

The only way to be sure that the peer is still reachable is to have some kind of heartbeat. This can be either be done at the application level or it can be done at the TCP level - using TCP keep-alive. Usually systems offer a way to not only enable TCP keep-alive per socket but also to adjust how often a keep-alive packet will be send when the socket is idle, i.e. how quick an application can find out that the peer is no longer there. To get to the details on how to do this in Python see How to change tcp keepalive timer using python script?.

How to set keepalive option for induvidual socket in VxWorks

From the VxWorks "Library Reference" manual (can be download):

OPTIONS FOR STREAM SOCKETS

The following sections discuss the socket options available for stream (TCP) sockets.

SO_KEEPALIVE -- Detecting a Dead Connection

Specify the SO_KEEPALIVE option to make the transport protocol (TCP) initiate a timer to detect a dead connection:

 setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof (optval));

This prevents an application from hanging on an invalid connection. The value at optval for this option is an integer (type int), either 1 (on) or 0 (off).
The integrity of a connection is verified by transmitting zero-length TCP segments triggered by a timer, to force a response from a peer node. If the peer does not respond after repeated transmissions of the KEEPALIVE segments, the connection is dropped, all protocol data structures are reclaimed, and processes sleeping on the connection are awakened with an ETIMEDOUT error.

The ETIMEDOUT timeout can happen in two ways. If the connection is not yet established, the KEEPALIVE timer expires after idling for TCPTV_KEEP_INIT. If the connection is established, the KEEPALIVE timer starts up when there is no traffic for TCPTV_KEEP_IDLE. If no response is received from the peer after sending the KEEPALIVE segment TCPTV_KEEPCNT times with interval TCPTV_KEEPINTVL, TCP assumes that the connection is invalid. The parameters TCPTV_KEEP_INIT, TCPTV_KEEP_IDLE, TCPTV_KEEPCNT, and TCPTV_KEEPINTVL are defined in the file target/h/net/tcp_timer.h.



Related Topics



Leave a reply



Submit