Created
August 20, 2024 08:49
-
-
Save vrbadev/9deecb9b5b66e6eb6604b57135ea2edd to your computer and use it in GitHub Desktop.
Simple Python socket server echoing all messages back to connected clients, keeping track of closed client threads
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
Created on Tue Aug 20 09:14:55 2024 | |
@author: Vojtech Vrba ([email protected]) | |
Simple socket client. | |
Connects to a server with an IP given by the current SSH connection, and with a port 12345. | |
Sends a sample message every 1 second and prints the server's response. | |
""" | |
import socket | |
import signal | |
import os | |
import time | |
import datetime | |
def format_time_addr(addr): | |
return "[%s|%s:%d]" % (datetime.datetime.now().strftime("%d.%m.%YT%H:%M:%S.%f"), addr[0], addr[1]) | |
def is_socket_closed(sock): | |
try: | |
# this will try to read bytes without blocking and also without removing them from buffer (peek only) | |
data = sock.recv(16, socket.MSG_DONTWAIT | socket.MSG_PEEK) | |
if len(data) == 0: | |
return True | |
except BlockingIOError: | |
return False # socket is open and reading from it would block | |
except ConnectionResetError: | |
return True # socket was closed for some other reason | |
except Exception as e: | |
print("unexpected exception when checking if a socket is closed:", e) | |
return False | |
return False | |
if __name__ == "__main__": | |
print("Starting socket client") | |
terminated = False | |
signal.signal(signal.SIGINT, lambda sig, frame: globals().update(terminated=True)) | |
ssh_ip = str(os.popen("echo $SSH_CONNECTION | awk '{print $1}'").read()).strip() | |
print("Server IP:", ssh_ip) | |
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) | |
client.connect((ssh_ip, 12345)) | |
addr = client.getsockname() | |
print(format_time_addr(addr), "Connected to the server") | |
while not terminated and not is_socket_closed(client): | |
client.send("Hello world!".encode("ascii")) | |
rxdata = client.recv(1024) | |
print(format_time_addr(addr), "Received: ", rxdata) | |
time.sleep(1) | |
print(format_time_addr(addr), "Stopping socket client") | |
client.close() | |
print("Script done.") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
""" | |
Created on Tue Aug 20 09:14:55 2024 | |
@author: Vojtech Vrba ([email protected]) | |
Simple socket server with threading support. | |
Listens at 0.0.0.0 (all interfaces) at port 12345. | |
Messages are only echoed back to clients. | |
""" | |
import socket | |
import threading | |
import signal | |
import datetime | |
def format_time_addr(addr): | |
return "[%s|%s:%d]" % (datetime.datetime.now().strftime("%d.%m.%YT%H:%M:%S.%f"), addr[0], addr[1]) | |
class SocketClientThread(threading.Thread): | |
def __init__(self, client_socket, addr): | |
threading.Thread.__init__(self) | |
self.socket = client_socket | |
self.address = addr | |
self.terminated = threading.Event() | |
def run(self): | |
while not self.terminated.is_set() and not self.is_disconnected(): | |
rxdata = self.socket.recv(1024) | |
if rxdata: | |
print(format_time_addr(self.address), "Received:", rxdata) | |
self.socket.send(b"Echo of: " + rxdata) | |
self.socket.close() | |
def is_disconnected(self): | |
try: | |
data = self.socket.recv(16, socket.MSG_DONTWAIT | socket.MSG_PEEK) | |
if len(data) == 0: | |
return True | |
except BlockingIOError: | |
return False # socket is open and reading from it would block | |
except ConnectionResetError: | |
return True # socket was closed for some other reason | |
except Exception as e: | |
print(format_time_addr(self.address), "unexpected exception when checking if a socket is closed:", e) | |
return False | |
def terminate(self): | |
self.terminated.set() | |
if __name__ == "__main__": | |
print("Starting socket server script") | |
terminated = False | |
signal.signal(signal.SIGINT, lambda sig, frame: globals().update(terminated=True)) | |
print("Initializing server socket") | |
server_addr = ("0.0.0.0", 12345) | |
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
server.settimeout(0.1) | |
server.bind(server_addr) | |
server.listen(1) | |
print(format_time_addr(server_addr), "Server is waiting for new connections...") | |
client_threads = dict() | |
while not terminated: | |
try: | |
client, addr = server.accept() | |
print(format_time_addr(addr), "Connection opened") | |
t = SocketClientThread(client, addr) | |
client_threads[addr] = t | |
t.start() | |
except socket.timeout: | |
pass | |
except: | |
raise | |
closed = list() | |
for a, t in client_threads.items(): | |
if not t.is_alive(): | |
print(format_time_addr(addr), "Connection closed") | |
closed.append(a) | |
for a in closed: client_threads.pop(a) | |
print(format_time_addr(server_addr), "Server closing...") | |
for a, t in client_threads.items(): | |
if t.is_alive(): | |
t.terminate() | |
t.join() | |
server.close() | |
print("Script done.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment