Search code examples
pythonnetwork-programmingtcpblockingrecv

python tcp socket - why sendall message is sent only after close()


Im trying to write perl TCP server / python TCP client, and i have the such code now:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ("127.0.0.1", 9000)
sock.connect(server_address)

try:
    message = unicode('Test')
    sock.sendall(message)
    data = sock.recv(1024)
    print data
finally:
    sock.close()

And i have noticed, that my TCP server (written in perl) is getting message not after sendall(message), but after close(). Server is working like an echo server, and sends data to client after getting a message. And that causes deadlock, server never gets a message, client never gets a response. What could be a problem? What is going to happen during close(), that message comes to server?


Solution

  • I'm going to hazard a guess that this is due to the server's implementation. There are many ways of writing an echo server:

    • receieve bytes in a loop (or async callback) until EOF; as the bytes are recieved (each loop iteration), echo them without any processing or buffering; when an EOF is found (the inbound stream is closed), close the outbound stream
    • read lines at a time (assume it is a text protocol), i.e. looking for CR / LF / EOF; when a line is found, return the line - when an EOF is found (the inbound stream is closed), close the outbound stream
    • read to an EOF; then return everything and close the outbound stream

    If the echo server uses the first approach, it will work as expected already - so we can discount that.

    For the second approach, you are sending text but no CR / LF, and you haven't closed the stream from client to server (EOF), so the server will never reply to this request. So yes, it will deadlock.

    If it is the third approach, then again - unless you close the outbound stream, it will deadlock.

    From your answer, it looks like adding a \n "fixes" it. From that, I conclude that your echo-server is line-based. So two solutions, and a third that would work in any scenario:

    1. make the echo-server respond to raw data, rather than lines
    2. add an end-of-line marker
    3. close the outbound stream at the client, i.e. the client-to-server stream (many network APIs allow you to close the outbound and inbound streams separately)

    Additionally: ensure Nagle is disabled (often called NO_DELAY) - this will prevent the bytes sitting at the client for a while, waiting to be composed into a decent sized packet (this applies to 1 & 2, but not 3; having Nagle enabled would add a delay, but will not usually cause a deadlock).