How can I monitor a TCP port for packets and timeout if none is received?
Question: I'm creating a test client with using python to test my server. It sends TCP
data packets to a server, which _should_ send back 1 response to the client in
the same TCP connection. If it does not send any response, or sends more than
1 response, then I have a bug in the server code.

What I want to do is to monitor the TCP port for response message(s) and print
them out. If there is no response, the script should carry on execution after
a timeout.

Here's the relevant code:

    
    
    import socket
    import time
    import multiprocessing
    
    
    def get_response(response_stream, response_buffer_size):
        response_count = 1
        while True:
            response = response_stream.recv(response_buffer_size).decode()
            print("[response]", response_count, ":", response)
            response_count += 1
    
    TCP_IP = '192.168.1.101'
    TCP_PORT = 12345
    BUFFER_SIZE = 1024
    MESSAGE = b"Bazinga!"
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((TCP_IP, TCP_PORT))
    s.send(MESSAGE.encode())
    
    print("Message sent.")
    
    p = multiprocessing.Process(target = get_response(s,BUFFER_SIZE))
    p.start()
    p.join(3)
    
    if p.is_alive():
        print("Killing thread ...")
        p.terminate()
        p.join()
    
    s.close()
    print("Socket closed.")
    

So it turns out that `p.join(3)` is never reached because the code is stuck
inside `get_response` at this line : `response =
response_stream.recv(response_buffer_size).decode()`.

The script will always wait there to decode the next packet, hence the script
never terminates properly. What am I doing wrong ?


Answer: First, this line:

    
    
    p = multiprocessing.Process(target = get_response(s,BUFFER_SIZE))
    

Is actually calling `get_response` _on the main thread_. Since, you're not
passing a function name like you should, but calling the function and passing
the `target` argument, the result of that call.

Instead you want

    
    
    p = multiprocessing.Process(target=get_response, args=(s,BUFFER_SIZE))
    

**But this won't work!** You can't easily pass a socket to a different
process. There are ways around it with pipes, but the implementation is only
there for Unix or Python3 on Windows.

So consider the different approach I list below.

Third, you might want to add something that breaks out of the infinite loop
inside your child process function. I added a line to check if `recv` returned
an empty string, you could modify it to your liking.

Consider:

    
    
    import socket
    import time
    import multiprocessing
    
    
    def worker(tcp_ip, tcp_port, response_buffer_size):
        MESSAGE = b"Bazinga!"
    
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((tcp_ip, tcp_port))
        s.send(MESSAGE.encode())
        print("Message sent.")
    
        response_count = 1
        while True:
            response = s.recv(response_buffer_size).decode()
            if len(response) == 0: break
            print("[response]", response_count, ":", response)
            response_count += 1
        # If we got here, the connection was closed
        s.close()
        print("Socket closed.")
    
    
    if __name__ == "__main__":
        TCP_IP = '192.168.1.10'
        TCP_PORT = 12345
        BUFFER_SIZE = 1024
    
        p = multiprocessing.Process(target=worker, args=(TCP_IP, TCP_PORT, BUFFER_SIZE))
        p.start()
        p.join(3)
    
        if p.is_alive():
            print("Killing thread ...")
            p.terminate()
            p.join()
            print("Thread terminated.")
    
        print("Goodbye.")
    

Here, we put _all_ socket activity in the worker thread. It owns and opens the
socket, sends the "Bazinga" and handles responses.

