What does socket.socket.send return, and why does sendall exist?
answer
- One call reports progress, the other insists
- A count comes back, not a flag
- A full kernel buffer truncates your write
- sendall returns None and raises on failure
- After a failed sendall the offset is unknown
basics
~20 ssocket.send hands bytes to the kernel's send buffer and returns how many it actually accepted, which can be fewer than you passed. socket.sendall loops internally until every byte is queued, returns None, and raises if the connection fails.
solid answer
~50 s`send()` is a thin wrapper over the system call: it copies as much as fits into the kernel's send buffer and returns that count. A **short write** happens when the buffer is full because the peer is slow or the TCP window is closed, so `send(data)` can return less than `len(data)` and the rest is silently dropped by your code if you ignore the return value. `sendall()` wraps the loop for you: it keeps calling `send` until everything is queued, returns `None`, and raises `OSError` (typically `BrokenPipeError` or `ConnectionResetError`) if the connection dies. Use `sendall` for application data. The one caveat: if `sendall` raises — especially on a socket with a timeout — it is unspecified how many bytes reached the peer, so the connection is no longer trustworthy. Close it and retry at the message level, never by resending on the same socket.
code
pycon · 9 lines>>> import socket
>>> a, b = socket.socketpair()
>>> a.send(b"1200 rows")
9
>>> a.sendall(b"1200 rows") is None
True
>>> b.recv(64)
b'1200 rows1200 rows'
>>> a.close(); b.close()go deeper
Remember the simple rule: use sendall for application data and let it raise on failure. Know that send returns a number of bytes, so ignoring that number can silently drop part of your message.
Explain when a short write actually happens — a full kernel send buffer because the peer is slow — and be able to write the send loop with a memoryview that sendall replaces.
Show that you know a failed sendall leaves an unknown number of bytes delivered, so recovery means closing the connection and resending the whole message under an idempotency rule, not resuming on the same socket.
Own the protocol-level answer to partial delivery: acknowledgements, idempotency keys and de-duplication windows, so that connection failure mid-message is a defined outcome rather than corrupt data discovered later.
**What `send` really is.** `socket.socket.send(data)` is essentially the `send(2)` system call. The kernel keeps a per-socket send buffer; `send` copies bytes into it and returns immediately with the number of bytes copied. It does **not** mean the peer received them — TCP will transmit and retransmit them later, entirely outside your call. The return value is the only thing telling you how much was accepted. **Why the count can be short.** The kernel buffer is finite. If the peer's application is not reading, its receive window closes; TCP stops accepting more, your send buffer fills, and the kernel takes only what fits. On a **blocking** socket, `send` waits until at least one byte can be copied, then returns however many it managed — often, but not reliably, all of them. On a socket in **timeout** mode or **non-blocking** mode the odds of a short write rise sharply, and on a non-blocking socket with a completely full buffer `send` raises `BlockingIOError` instead of returning zero. Because the common case on a fast local connection is "it took everything", ignoring the return value passes tests and then truncates messages in production exactly when the peer is under pressure — which is precisely when you least want mangled data. **The correct manual loop.** Writing it once is the best way to understand `sendall`: ```python def send_all(sock, data): view = memoryview(data) while view: sent = sock.send(view) view = view[sent:] ``` The `memoryview` matters: slicing `bytes` would copy the remaining tail on every iteration, turning a large payload into quadratic work, while slicing a `memoryview` is a cheap re-window over the same buffer. `sendall` is the C implementation of this loop. **What `sendall` guarantees, and what it does not.** On success, every byte is in the kernel's buffer and `sendall` returns `None` — a real trap for anyone who writes `if sock.sendall(data):`, since `None` is always falsy. On failure it raises. `BrokenPipeError` means you wrote to a connection the peer has already closed for reading; `ConnectionResetError` means the peer sent an RST. What `sendall` cannot tell you is **how far it got**. The documentation is explicit that on error, and in particular on a timeout, it is unspecified how much data was sent. That is not a Python wart; the information genuinely does not exist at that layer. The operational consequence is firm: after a failed `sendall`, the socket's byte stream is at an unknown offset, so the only safe move is to close the connection and retry the whole message on a fresh one — with an idempotency key or a de-duplication rule at the protocol level, since the peer may have received a prefix, all of it, or nothing. **Related knobs.** Nagle's algorithm makes the kernel coalesce small writes while an earlier segment is unacknowledged, trading latency for fewer packets. If your protocol is many small request/response exchanges, this shows up as puzzling round-trip delays, and `sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)` disables it. The better fix is usually to stop making many tiny writes: build one `bytes` object — header plus body — and issue a single `sendall`, which is both faster and immune to interleaving problems when several threads share a socket. For sending a file, `socket.socket.sendfile(file)` lets the OS move the data without pulling it through Python. And `send` has no partner obligation to the receiver: the peer's `recv` boundaries have nothing to do with your `send` boundaries, which is the framing problem the read side has to solve. **Rules of thumb.** Use `sendall` for anything you care about. Reach for raw `send` only when you are running your own readiness loop and must track partial progress yourself. Never assume a successful `sendall` means the peer *processed* the message — it means the bytes are queued locally. If you need delivery confirmation, put it in the protocol as an acknowledgement, not in the socket call. **Threads and one shared socket.** Nothing in the socket API makes interleaved writes safe. Two threads calling `sendall` on the same connection can have their byte ranges interleaved by the kernel, producing a stream that satisfies TCP perfectly and violates your protocol completely. Either give each thread its own connection, or serialise writes behind a `threading.Lock` and hold it for the whole message rather than per chunk. This is one more argument for assembling each message into a single `bytes` object before writing it.
- A sendall call raised a timeout part-way through a message. Can you retry it on the same socket?No. It is unspecified how many bytes reached the peer, so the stream is at an unknown offset and any resend would either duplicate or corrupt the message. Close the connection, open a fresh one, and resend the whole message — which only works if the protocol tolerates duplicates, via an idempotency key or a sequence number the peer de-duplicates on. Silently retrying on the same socket is a classic source of garbled payloads.
- Why does the manual send loop slice a memoryview rather than the bytes object?Slicing `bytes` allocates and copies the remaining tail on every iteration, so a large payload sent in many chunks becomes quadratic in the payload size. A `memoryview` slice is a new window over the same underlying buffer with no copy, so the loop stays linear. `send` accepts any object supporting the buffer protocol, so passing the view directly works.
- Your protocol does many small request/response exchanges and you see unexplained latency. What socket option is worth checking?Nagle's algorithm, which delays small writes while an earlier segment is unacknowledged so it can coalesce them. Disable it with `sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)`. Before reaching for the option, check whether the code makes several small writes per message — assembling header and body into one bytes object and issuing a single sendall usually fixes both the latency and any interleaving risk.
send is a courier who takes as many parcels as fit in the van and hands you a receipt for that many; sendall keeps sending vans until the pile is gone, but if the road closes mid-way nobody can tell you which parcels arrived.
saying these in an interview costs you the question
- Assumes send always transmits the whole buffer
- Ignores the integer send returns
- Thinks sendall returning None signals failure
- Believes a successful send means the peer received it
- Retries a failed sendall on the same socket
- Slices bytes instead of a memoryview in the send loop