My goal is to emit an interface to listen on a socket forever … until someone up the decision chain decides it’s enough.
This is my implementation, it does not work. Mixing threads, sockets, object lifetime, default params and a language I do not speak too well is confusing.
I tested individually different aspects of this code and everything was as expected except the line containing the comment BUG
where I attempt to force the main thread to block until the server hears the child screaming or a timeout passes but instead recv()
simply doesn’t see the change in alive
.
#!/usr/bin/env python3
import socket
import threading
import time
MAX_MSG_BYTES=1024
TEST_PORT=42668
def recv( s: socket.socket, alive: bool=True ) -> bytes:
'''
Accepts packets on a socket until terminated.
'''
s.settimeout(1) # 1 second
while alive:
print("'alive' is still", alive)
try:
data = s.recv(MAX_MSG_BYTES)
assert data # Empty packets were a problem.
yield data
except TimeoutError:
pass # expected error, any other is propagated up
def test_nonblocking_recv() -> None:
# Create 3 sockets - sever administrative, server content and client content.
# Bind the latter and forget about the former.
server_s = socket.create_server(('', TEST_PORT))
server_s.listen()
client_s = socket.create_connection(('localhost', TEST_PORT))
content_s = next(iter(server_s.accept())) # Accept 1 connection.
# client_s.sendall('If this is commented out, the server hangs.'.encode('utf8'))
alive = True
def read_one_message():
data = recv(content_s, alive)
print(next(iter(data))) # BUG this causes outside alive to not be seen
content_th = threading.Thread(target=read_one_message)
content_th.start()
time.sleep(3)
alive = False
print("But main thread 'alive' is", alive)
content_th.join()
assert threading.active_count() == 1
if __name__ == '__main__':
test_nonblocking_recv()
4