|
| 1 | +# Multiple TCP Echo Server |
| 2 | +# Based on the third example from: |
| 3 | +# https://rosettacode.org/wiki/Echo_server#Python |
| 4 | + |
| 5 | +#!usr/bin/env python |
| 6 | +import socket |
| 7 | +import threading |
| 8 | + |
| 9 | +NUM_PORTS = 5 # Echo on this many ports starting at PORT_BASE |
| 10 | +PORT_BASE = 1200 # Change this if required |
| 11 | +HOST = '192.168.0.50' # Change this to match your local IP |
| 12 | +SOCKET_TIMEOUT = 30 |
| 13 | + |
| 14 | +# This function handles reading data sent by a client, echoing it back |
| 15 | +# and closing the connection in case of timeout (30s) or "quit" command |
| 16 | +# This function is meant to be started in a separate thread |
| 17 | +# (one thread per client) |
| 18 | +def handle_echo(client_connection, client_address, port): |
| 19 | + client_connection.settimeout(SOCKET_TIMEOUT) |
| 20 | + try: |
| 21 | + while True: |
| 22 | + data = client_connection.recv(1024) |
| 23 | + # Close connection if "quit" received from client |
| 24 | + if data == b'quit\r\n' or data == b'quit\n': |
| 25 | + print('{} : {} disconnected'.format(client_address,port)) |
| 26 | + client_connection.shutdown(1) |
| 27 | + client_connection.close() |
| 28 | + break |
| 29 | + # Echo back to client |
| 30 | + elif data: |
| 31 | + print('FROM {} : {} : {}'.format(client_address,port,data)) |
| 32 | + client_connection.send(data) |
| 33 | + # Timeout and close connection after 30s of inactivity |
| 34 | + except socket.timeout: |
| 35 | + print('{} : {} timed out'.format(client_address,port)) |
| 36 | + client_connection.shutdown(1) |
| 37 | + client_connection.close() |
| 38 | + |
| 39 | +# This function opens a socket and listens on specified port. As soon as a |
| 40 | +# connection is received, it is transfered to another socket so that the main |
| 41 | +# socket is not blocked and can accept new clients. |
| 42 | +def listen(host, port): |
| 43 | + # Create the main socket (IPv4, TCP) |
| 44 | + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 45 | + connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 46 | + connection.bind((host, port)) |
| 47 | + # Listen for clients (max 10 clients in waiting) |
| 48 | + connection.listen(10) |
| 49 | + # Every time a client connects, allow a dedicated socket and a dedicated |
| 50 | + # thread to handle communication with that client without blocking others. |
| 51 | + # Once the new thread has taken over, wait for the next client. |
| 52 | + while True: |
| 53 | + current_connection, client_address = connection.accept() |
| 54 | + print('{} : {} connected'.format(client_address, port)) |
| 55 | + handler_thread = threading.Thread( \ |
| 56 | + target = handle_echo, \ |
| 57 | + args = (current_connection,client_address,port) \ |
| 58 | + ) |
| 59 | + # daemon makes sure all threads are killed if the main server process |
| 60 | + # gets killed |
| 61 | + handler_thread.daemon = True |
| 62 | + handler_thread.start() |
| 63 | + |
| 64 | +if __name__ == "__main__": |
| 65 | + print('starting') |
| 66 | + |
| 67 | + threads = list() |
| 68 | + |
| 69 | + for i in range(NUM_PORTS): |
| 70 | + threads.append( threading.Thread( \ |
| 71 | + target = listen, args = (HOST, PORT_BASE + i)) ) |
| 72 | + threads[i].daemon = True |
| 73 | + threads[i].start() |
| 74 | + |
0 commit comments