During my experience working with Python, I’ve had several cases where a network client was left hanging while trying to request a server. After spending some time researching, the root cause was identified and it was found that the server was waiting for a response but there was silence (like being _ghosted _on a Tinder match).

Image for post

[Photo on masculino.ru]

Of course, an operating system can raise a Connection timed out error, but it doesn’t always seem to happen on hang connections. If it did, probably wouldn’t even run into this issue on the job.

In order to avoid the annoying problem of waiting for an undetermined amount of time, sockets (which are responsible for network communication) support a timeout option which raises an error based on a time limit. Unfortunately, when developers develop their amazing network libraries, they may omit such non-obvious cases and forget to provide socket timeout settings, despite of the docs recommendation:

The [_connect()_](https://docs.python.org/3/library/socket.html#socket.socket.connect) operation is also subject to the timeout setting, and in general it is recommended to call [_settimeout()_](https://docs.python.org/3/library/socket.html#socket.socket.settimeout) before calling [_connect()_](https://docs.python.org/3/library/socket.html#socket.socket.connect) or pass a timeout parameter to [_create_connection()_](https://docs.python.org/3/library/socket.html#socket.create_connection). However, the system network stack may also return a connection timeout error of its own regardless of any Python socket timeout setting.

Let’s look at a simple example of how to simulate hang socket (at a minimum level in Mac OS):

## server side
import socket
server = socket.socket()
server.bind(('127.0.0.1', 30000))
## client side
import socket
client = socket.socket()
client.connect(('127.0.0.1', 30000))  ## hang :(

At this moment, the client is hanging, waiting for the server to enable to connection accepting, like server.listen(1).

It won’t actually hang for eternity, and after some time should eventually raise a TimeoutError: [Errno 60] Operation timed out. This happens because the system function timed out at the system level with the error ETIMEDOUT. As mentioned above, it seems the ETIMEDOUT error doesn’t always happen or it could be that the timeout value can be obscenely large. In either case, it’s not controlled in your code. The variant which I will show has been much more reliable for me.

Before I go into showing issue solution, let’s go deep into Python socket implementation in order to understand why it hangs.

  1. [sock_connect](https://github.com/python/cpython/blob/v3.7.5/Modules/socketmodule.c#L3094) is a C-function, which is called on client.connect. Its code is clear and we see that it calls ↓
  2. [internal_connect](https://github.com/python/cpython/blob/v3.7.5/Modules/socketmodule.c#L3026) function, and this code is harder to understand, but I will give a hint — we are interesting in ↓
  3. [sock_call_ex](https://github.com/python/cpython/blob/v3.7.5/Modules/socketmodule.c#L815) function, which has an eternal cycle [while (1) {](https://github.com/python/cpython/blob/v3.7.5/Modules/socketmodule.c#L833), where socket communication happens and where it tries to wait for an established connection as well.
  4. Also in sock_call_ex is a processes timeout option in [if (has_timeout) {](https://github.com/python/cpython/blob/v3.7.5/Modules/socketmodule.c#L837). If a timeout is achieved it returns the error: [PyErr_SetString(socket_timeout, “timed out”);](https://github.com/python/cpython/blob/v3.7.5/Modules/socketmodule.c#L884).

Let’s see how the timeout option helps with a hang socket:

## server side
import socket
server = socket.socket()
server.bind(('127.0.0.1', 30000))
## client side
import socket
client = socket.socket()
client.settimeout(3)
client.connect(('127.0.0.1', 30000))
----> 1 client.connect(('127.0.0.1', 30000))
timeout: timed out

#time-out #tricks #engineering #python #development

Socket Timeout — An Important, but Not Simple, Issue with Python
15.85 GEEK