In the first example we will create a simple echo server allowing one connection and a simple echo client, in the second example we will improve the first example allowing multiple client to be connected.
Version used in this tutorial:
Python: 3.7+
NOTE: In all scripts is present the hash bang for MacOS, you don't need it if you call the script using the python interpreter, i.e.: python my_test_server.py
Alternatively you can change the interpreter path in order to match the desidered one (i.e. /bin/python) and execute it directly (like: ./my_test_server.py of course after giving the execute permission with "chmod +x my_test_server.py")
Simple echo server:
#!/usr/bin/env python3 # Echo server import socket HOST = '' # all available interfaces PORT = 5005 # any port > 1023 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(1) # 1 means the number of accepted connections conn, addr = s.accept() # waits for a new connection print('Client connected: ', addr) while True: data = conn.recv(1024) if not data: break conn.sendall(data) conn.close()
Simple echo client
The client connects the same server port and accepts user input until input data is null (i.e. ENTER key pressed). Any input data is converted to string and sent to the server as bytes, then waits to receive data from server, then prints echoed data to console.#!/usr/bin/env python3 # Echo client import socket HOST = '' # all available interfaces PORT = 5005 # any port > 1023 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) print('Client connected.') while True: user_input = str(input()) # convert input to string if not user_input: # i.e. enter key pressed break s.sendall(user_input.encode()) # encode needed to transform string to bytes data = s.recv(1024) if not data: break print("Data received from server: ", data.decode('utf-8')) # decode needed to convert data to string
Echo server allowing multiple connections
The first echo server can be improved allowing multiple connections. The accept() method blocks the current execution. We can send the accepted connection to a separate thread that will manage the task with the given client, while then the main socket can be ready again to accept a new client.In order to stop the server gracefully, we will set the stop_main_thread variable and in order to unlock the accept() method we will send a dummy connection request.
#!/usr/bin/env python3 # Echo server import socket import threading HOST = '' # all available interfaces PORT = 5005 # any port > 1023 main_socket: socket.socket = None stop_main_thread = False def on_client_connected(conn: socket.socket, addr): print('Client connected: ', addr) while True: data = conn.recv(1024) if not data: break conn.sendall(data) conn.close(); print('Client disconnected: ', addr) def start_server(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(10) # 10 means the number of accepted connections global main_socket global stop_main_thread main_socket = s print(f'Server started on port {PORT}, waiting for connections... (Press ENTER key to stop)') while True and not stop_main_thread: try: conn, addr = s.accept() # waits for a new connection threading.Thread(target=on_client_connected, args=(conn, addr)).start() except: pass print('Server stopped.') def stop_server(): global stop_main_thread stop_main_thread = True # makes a dummy connection just to unlock accept method and trigger the gracefully stop socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((HOST, PORT)) main_thread = threading.Thread(target=start_server) main_thread.start() while True: user_input = input() if not user_input: # i.e. enter key pressed break print('Stopping server... ') stop_server() main_thread.join() main_socket.close()
You can use the same client to connect to this server.
This is just a simple example and it can be improved a lot, i.e. my managing sent ad received data chunks in a better way.
For more information about sockets:
https://docs.python.org/3/howto/sockets.html
No comments:
Post a Comment