Synopsis: server hangs on socket.recv() even though a socket.settimeout() was set.
Have a simple socket based server that loops over commands (simple text messages) sent from client(s) until it receives an 'end' command. I simulate a broken client by - making a connection - sending a command - receive the result - exit (never sending the 'end' command to server)
The whole system worked perfectly when the server/client protocol was adhered to, but with the broken client simulation the server is NOT timing out on the recv.
Relevant server code (simplified):
ip = '192.168.77.170'
port = 12345
args = { app specific data }
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as serverSock:
serverSock.bind((ip, port))
serverSock.listen()
while True:
sock, addr = serverSock.accept()
thread = threading.Thread(target=session, args=(sock, args))
thread.name = "client {}".format(addr)
thread.daemon = True
thread.start()
def session(sock, args):
sess = threading.local()
sess.sock = sock
sess.__dict__.update(args)
print("-" * 60)
print("Session opened from={addr}.".format(addr=sock.getpeername()))
sock.settimeout(10.0)
while True:
try:
print('about to wait on recv', sock.gettimeout())
cmd = recvString(sock)
print('got cmd =', cmd)
if cmd.startswith('foo'): doFoo(sess, cmd)
elif cmd.startswith('bar'): doBar(sess, cmd)
elif cmd.startswith('baz'): doBaz(sess, cmd)
elif cmd.startswith('end'): break
else:
raise Exception("Protocol Error: bad command '{}'".format(cmd))
except TimeoutError as err:
print("Error protocol timeout")
break
finally:
try:
sock.close()
except:
pass
print("Session closed.")
def recvString(sock):
buff = bytearray()
while True:
b = sock.recv(1)
if b == b'\x00': break
buff += b
return buff.decode() if len(buff) else ''
When running with broken client I get
> about to wait on recv cmd 10.0
> got cmd = foo
> about to wait on recv cmd 10.0
waits forever (and has very high CPU consumption to add insult to injury)
I've RTFM'd thoroughly and multiple times, and looked at other similar SO postings and all indicate that a simple settimeout() should work. Can't see what I'm doing wrong. Any help greatly appreciated.
while True:
b = sock.recv(1)
if b == b'\x00': break
buff += b
If the peer closes the connection then sock.recv(1)
will return b''
, i.e. no data. This situation is not accounted for.
As a result there will be an endless and busy loop where sock.recv(1)
will return with b''
, only to be called again and immediately return with b''
etc. This also explains the high CPU.