server.py
import socket from threading import Thread # server's IP address SERVER_HOST = "0.0.0.0" SERVER_PORT = 5002 # port we want to use separator_token = "<SEP>" # we will use this to separate the client name & message # initialize list/set of all connected client's sockets client_sockets = set() # create a TCP socket s = socket.socket() # make the port as reusable port s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # bind the socket to the address we specified s.bind((SERVER_HOST, SERVER_PORT)) # listen for upcoming connections s.listen(5) print(f"[*] Listening as {SERVER_HOST}:{SERVER_PORT}") def listen_for_client(cs): """ This function keep listening for a message from `cs` socket Whenever a message is received, broadcast it to all other connected clients """ while True: try: # keep listening for a message from `cs` socket msg = cs.recv(1024).decode() except Exception as e: # client no longer connected # remove it from the set print(f"[!] Error: {e}") client_sockets.remove(cs) else: # if we received a message, replace the <SEP> # token with ": " for nice printing msg = msg.replace(separator_token, ": ") # iterate over all connected sockets for client_socket in client_sockets: # and send the message client_socket.send(msg.encode()) while True: # we keep listening for new connections all the time client_socket, client_address = s.accept() print(f"[+] {client_address} connected.") # add the new connected client to connected sockets client_sockets.add(client_socket) # start a new thread that listens for each client's messages t = Thread(target=listen_for_client, args=(client_socket,)) # make the thread daemon so it ends whenever the main thread ends t.daemon = True # start the thread t.start() # close client sockets for cs in client_sockets: cs.close() # close server socket s.close()
client.py
import socket import random from threading import Thread from datetime import datetime from colorama import Fore, init, Back # init colors init() # set the available colors colors = [Fore.BLUE, Fore.CYAN, Fore.GREEN, Fore.LIGHTBLACK_EX, Fore.LIGHTBLUE_EX, Fore.LIGHTCYAN_EX, Fore.LIGHTGREEN_EX, Fore.LIGHTMAGENTA_EX, Fore.LIGHTRED_EX, Fore.LIGHTWHITE_EX, Fore.LIGHTYELLOW_EX, Fore.MAGENTA, Fore.RED, Fore.WHITE, Fore.YELLOW ] # choose a random color for the client client_color = random.choice(colors) # server's IP address # if the server is not on this machine, # put the private (network) IP address (e.g 192.168.1.2) SERVER_HOST = "127.0.0.1" SERVER_PORT = 5002 # server's port separator_token = "<SEP>" # we will use this to separate the client name & message # initialize TCP socket s = socket.socket() print(f"[*] Connecting to {SERVER_HOST}:{SERVER_PORT}...") # connect to the server s.connect((SERVER_HOST, SERVER_PORT)) print("[+] Connected.") # prompt the client for a name name = input("Enter your name: ") def listen_for_messages(): while True: message = s.recv(1024).decode() print("\n" + message) # make a thread that listens for messages to this client & print them t = Thread(target=listen_for_messages) # make the thread daemon so it ends whenever the main thread ends t.daemon = True # start the thread t.start() while True: # input message we want to send to the server to_send = input() # a way to exit the program if to_send.lower() == 'q': break # add the datetime, name & the color of the sender date_now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') to_send = f"{client_color}[{date_now}] {name}{separator_token}{to_send}{Fore.RESET}" # finally, send the message s.send(to_send.encode()) # close the socket s.close()