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