Skip to main content

Modules/socketmodule.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/socketmodule.c

This annotation covers the network I/O operations. See modules_socket_detail for socket creation, binding, and address families.

Map

LinesSymbolRole
1-200sock_connectTCP connect with optional timeout
201-400sock_acceptAccept incoming connection
401-700sock_recv, sock_recvfromReceive bytes, optionally with address
701-1000sock_send, sock_sendto, sock_sendallSend bytes
1001-1200sock_makefileWrap socket in a file-like object
1201-1400sock_setsockopt, sock_getsockoptSocket options
1401-1600sock_setblocking, sock_settimeoutBlocking mode and timeout
1601-2000socket_getaddrinfoDNS + address resolution
2001-2200socket_getnameinfoReverse lookup
2201-2400socket_gethostname, socket_gethostbynameHost name utilities

Reading

connect with timeout

// CPython: Modules/socketmodule.c:145 sock_connect
static PyObject *
sock_connect(PySocketSockObject *s, PyObject *addro)
{
sock_addr_t addrbuf;
int addrlen;
if (!getsockaddrarg(s, addro, &addrbuf, &addrlen, "connect")) return NULL;
Py_BEGIN_ALLOW_THREADS
do {
res = connect(s->sock_fd, (struct sockaddr *)&addrbuf, addrlen);
} while (res < 0 && errno == EINTR && !(s->sock_timeout > 0.0));
Py_END_ALLOW_THREADS
if (res < 0 && errno == EINPROGRESS && s->sock_timeout > 0.0) {
/* Non-blocking connect: wait for writability */
res = internal_select(s, 1 /* write */, s->sock_timeout);
...
}
...
}

Py_BEGIN_ALLOW_THREADS releases the GIL during the blocking connect() call. EINTR is retried unless a timeout is set.

recv with buffer size

// CPython: Modules/socketmodule.c:445 sock_recv_impl
static PyObject *
sock_recv_impl(PySocketSockObject *s, Py_ssize_t recvlen, int flags)
{
PyObject *buf = PyBytes_FromStringAndSize(NULL, recvlen);
Py_BEGIN_ALLOW_THREADS
outlen = recv(s->sock_fd, PyBytes_AS_STRING(buf), recvlen, flags);
Py_END_ALLOW_THREADS
if (outlen < recvlen)
_PyBytes_Resize(&buf, outlen); /* shrink to actual bytes received */
return buf;
}

sendall

// CPython: Modules/socketmodule.c:820 sock_sendall
static PyObject *
sock_sendall(PySocketSockObject *s, PyObject *args)
{
const char *buf = PyBytes_AS_STRING(data);
Py_ssize_t len = PyBytes_GET_SIZE(data);
while (len > 0) {
Py_BEGIN_ALLOW_THREADS
n = send(s->sock_fd, buf, len, flags);
Py_END_ALLOW_THREADS
if (n < 0) {
if (errno == EINTR) continue;
...
return NULL;
}
buf += n;
len -= n;
}
Py_RETURN_NONE;
}

sendall loops until all bytes are delivered, handling short sends.

getaddrinfo

// CPython: Modules/socketmodule.c:1680 socket_getaddrinfo
static PyObject *
socket_getaddrinfo(PyObject *self, PyObject *args, PyObject *kwargs)
{
struct addrinfo hints, *res0, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = socktype;
hints.ai_protocol = proto;
hints.ai_flags = flags;
Py_BEGIN_ALLOW_THREADS
error = getaddrinfo(hptr, pptr, &hints, &res0);
Py_END_ALLOW_THREADS
/* Build list of (family, type, proto, canonname, sockaddr) tuples */
...
}

socket.getaddrinfo('python.org', 443) returns a list of (AF_INET, SOCK_STREAM, 6, '', ('151.101.x.x', 443)) tuples.

Timeout handling

// CPython: Modules/socketmodule.c:1440 sock_settimeout
static PyObject *
sock_settimeout(PySocketSockObject *s, PyObject *arg)
{
if (arg == Py_None) {
s->sock_timeout = -1.0; /* blocking */
internal_setblocking(s, 1);
} else {
s->sock_timeout = PyFloat_AsDouble(arg);
internal_setblocking(s, 0); /* non-blocking for select-based timeout */
}
Py_RETURN_NONE;
}

A socket with a timeout is set to non-blocking mode at the OS level; Python implements the timeout via select/poll.

gopy notes

socket is in module/socket/. connect, recv, send, accept use Go's net package. The GIL release (Py_BEGIN_ALLOW_THREADS) maps to releasing Go's per-goroutine Python lock. Timeouts use net.Conn.SetDeadline. getaddrinfo uses net.LookupHost + net.LookupPort. Socket options use syscall.SetsockoptInt.