1

I copied the echo server example from the python documentation and it's working fine. But when I edit the code, so it wont send the data back to the client, the socket.recv() method doesn't return when it's called the second time.

import socket

HOST = ''
PORT = 50007 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)

conn, addr = s.accept()
print('Connected by', addr)

while True:
    data = conn.recv(1024)
    if not data: break
conn.sendall(b'ok')
conn.close()

In the original version from the python documentation the while loop is slightly different:

while True:
    data = conn.recv(1024)
    if not data: break
    conn.sendall(data)

Client's code:

import socket

HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
s.close()
print('Received', repr(data))
no0by5
  • 632
  • 3
  • 8
  • 30
  • Where's the client's source code? Are you just connecting directly with `nc` or `telnet`? – Kevin Apr 14 '17 at 22:40
  • The receive won't return until the client closes the connection. But the client won't close the connection until it receives something. – tdelaney Apr 14 '17 at 22:47
  • I first want to close the connection after the client received an OK by the server... how can I do this in my case? – no0by5 Apr 14 '17 at 22:49
  • Can you explain why you are calling `recv` a second time? Also, why does the client close the socket without ensuring it has received everything the server intends to send? – David Schwartz Apr 14 '17 at 22:50
  • For the case the client is sending larger data and the buffer of 1024 isn't big enough. – no0by5 Apr 14 '17 at 22:51

2 Answers2

4

TCP sockets are streams of data. There is no one-to-one correlation between send calls on one side and receive calls on the other. There is a higher level correlation based on the protocol you implement. In the original code, the rule was that the server would send exactly what it received until the client closed the incoming side of the connection. Then the server closed the socket.

With your change, the rules changed. Now the server keeps receiving and discarding data until the client closes the incoming side of the connection. Then the server sends "ok" and closes the socket.

A client using the first rule hangs because its expecting data before it closes the socket. If it wants to work with this new server rule, it has to close its outgoing side of the socket to tell the server its done, and then it can get the return data.

I've updated the client and server to shutdown parts of the connection and also have the client do multiple recv's in case the incoming data is fragmented. Less complete implementations seem to work for small payloads because you are unlikely to get fragmentation, but break horribly in real production code.

server

import socket

HOST = ''
PORT = 50007 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)

conn, addr = s.accept()
print('Connected by', addr)

while True:
    data = conn.recv(1024)
    if not data: break
conn.sendall(b'ok')
conn.shutdown(socket.SHUT_WR)
conn.close()

client

import socket

HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
s.shutdown(socket.SHUT_WR)
data = b''
while True:
    buf = s.recv(1024)
    if not buf:
        break
    data += buf
s.close()
print('Received', repr(data))
tdelaney
  • 73,364
  • 6
  • 83
  • 116
3

The number of receive and send operations have to match because they are blocking. This is the flow diagram for your code:

Server listen 
Client connect 
Server receive (this waits until a message arrives at the server) [1] 
Client send 'Hello world' (received by [1])
Server receive (because there was data received) [2]
Client receive [3]

Because the server and the client are blocked now, no program can continue any further.

The fix would be to remove the client's receive call because you removed the server's send call.

Sekuraz
  • 548
  • 3
  • 15
  • Ok, thank you! Is there a way how i can send an answer to the client after receiving all data? – no0by5 Apr 14 '17 at 22:55
  • 1
    @no0by5: The client needs to tell the server how much data it is going to send, so that the server knows what value to pass to `recv()`. Right now, the server is just trying to receive "everything the client sends until the client closes the connection," which doesn't work very well if the client is going to stop sending and wait for a reply rather than closing the connection. – Kevin Apr 14 '17 at 23:00