├── README.md ├── hangman ├── words.csv ├── client.py ├── server.py └── hangman.py ├── chat ├── tcp_client.py └── tcp_server.py └── tiny_rmi ├── client.py └── server.py /README.md: -------------------------------------------------------------------------------- 1 | # python-networking 2 | Experimenting with sockets in Python. 3 | 4 | Feel free to fork and add any examples if you like. 5 | -------------------------------------------------------------------------------- /hangman/words.csv: -------------------------------------------------------------------------------- 1 | father,door,thing,police,number,fox,not,poor,rod,save,time,phone 2 | tipsy,vote,oil,paper,glasses,laptop,computer,dictionary,remote,screen,cat,pen 3 | -------------------------------------------------------------------------------- /chat/tcp_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | """ TCP Client """ 3 | 4 | import socket 5 | import select 6 | import sys 7 | 8 | if len(sys.argv) < 3: 9 | print("Usage : python {0} hostname port".format(sys.argv[0])) 10 | sys.exit() 11 | 12 | HOST = sys.argv[1] 13 | PORT = int(sys.argv[2]) 14 | 15 | MASTER_SOCK = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 | MASTER_SOCK.settimeout(200) 17 | 18 | # connect to remote host 19 | try: 20 | MASTER_SOCK.connect((HOST, PORT)) 21 | except Exception as msg: 22 | print(type(msg).__name__) 23 | print("Unable to connect") 24 | sys.exit() 25 | 26 | print("Connected to remote host. Start sending messages") 27 | 28 | while True: 29 | SOCKET_LIST = [sys.stdin, MASTER_SOCK] 30 | # Get the list sockets which are readable 31 | READ_SOCKETS, WRITE_SOCKETS, ERROR_SOCKETS = select.select(SOCKET_LIST, [], []) 32 | 33 | for sock in READ_SOCKETS: #incoming message from remote server 34 | if sock == MASTER_SOCK: 35 | data = sock.recv(4096) 36 | if not data: 37 | print('\nDisconnected from chat server') 38 | sys.exit() 39 | else: # print data 40 | print(data.decode(), end="") 41 | else: #user entered a message 42 | msg = sys.stdin.readline() 43 | print("\x1b[1A" + "\x1b[2K", end="") # erase last line 44 | MASTER_SOCK.sendall(msg.encode()) 45 | -------------------------------------------------------------------------------- /hangman/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | """ TCP Client """ 3 | 4 | import socket 5 | import select 6 | import sys 7 | 8 | if len(sys.argv) < 3: 9 | print("Usage : python {0} hostname port".format(sys.argv[0])) 10 | sys.exit() 11 | 12 | HOST = sys.argv[1] 13 | PORT = int(sys.argv[2]) 14 | 15 | MASTER_SOCK = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 | MASTER_SOCK.settimeout(200) 17 | 18 | # connect to remote host 19 | try: 20 | MASTER_SOCK.connect((HOST, PORT)) 21 | except Exception as msg: 22 | print(type(msg).__name__) 23 | print("Unable to connect") 24 | sys.exit() 25 | 26 | print("Connected to remote host. Start sending messages") 27 | 28 | while True: 29 | SOCKET_LIST = [sys.stdin, MASTER_SOCK] 30 | # Get the list sockets which are readable 31 | READ_SOCKETS, WRITE_SOCKETS, ERROR_SOCKETS = select.select(SOCKET_LIST, [], []) 32 | 33 | for sock in READ_SOCKETS: #incoming message from remote server 34 | if sock == MASTER_SOCK: 35 | data = sock.recv(4096) 36 | if not data: 37 | print('\nDisconnected from chat server') 38 | sys.exit() 39 | else: # print data 40 | print(data.decode(), end="") 41 | else: #user entered a message 42 | msg = sys.stdin.readline() 43 | print("\x1b[1A" + "\x1b[2K", end="") # erase last line 44 | MASTER_SOCK.sendall(msg.encode()) 45 | -------------------------------------------------------------------------------- /tiny_rmi/client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import json 3 | 4 | 5 | class ClientException(Exception): 6 | def __init__(self, message): 7 | super().__init__(message) 8 | 9 | 10 | class RemoteException(Exception): 11 | def __init__(self, message): 12 | super().__init__(message) 13 | 14 | 15 | class Client: 16 | __ip = None 17 | __port = None 18 | __connected = False 19 | __received_data = None 20 | __socket = None 21 | 22 | def __init__(self, **kwargs): 23 | self.__ip = kwargs["ip"] 24 | self.__port = kwargs["port"] 25 | self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 26 | 27 | def __handle_received_data(self): 28 | if self.__received_data: 29 | json_dict = json.loads(self.__received_data) 30 | 31 | if json_dict.get("error", False): 32 | raise RemoteException("Exception: {}".format(json_dict.get("message", None))) 33 | 34 | return json_dict.get("return", None) 35 | else: 36 | raise ClientException("Response is empty!") 37 | 38 | def remote_call(self, method, **kwargs): 39 | if self.__connected: 40 | # Make the method call 41 | data_to_send = { 42 | "method": method, 43 | "parameters": kwargs 44 | } 45 | self.__socket.sendall(json.dumps(data_to_send).encode()) 46 | # Handle response 47 | data = self.__socket.recv(4096) 48 | if not data: 49 | raise ClientException("Error receiving data from remote end! Broken pipe.") 50 | else: 51 | self.__received_data = data.decode() 52 | return self.__handle_received_data() 53 | 54 | else: 55 | raise ClientException("Currently not connected!") 56 | 57 | def connect(self): 58 | try: 59 | self.__socket.connect((self.__ip, self.__port)) 60 | self.__connected = True 61 | except Exception: 62 | raise ClientException( 63 | "Could not connect to remote host! ip: {0} server: {1}\n".format(self.__ip, self.__port)) 64 | 65 | 66 | def main(): 67 | client = Client(ip="127.0.0.1", port=6666) 68 | client.connect() 69 | 70 | x = client.remote_call("get_global_dummy") 71 | print(x) 72 | x = client.remote_call("set_global_dummy", value=10) 73 | print(x) 74 | x = client.remote_call("get_global_dummy") 75 | print(x) 76 | 77 | 78 | if __name__ == '__main__': 79 | main() 80 | -------------------------------------------------------------------------------- /chat/tcp_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | """ A simple chat TCP server """ 3 | import socket 4 | import select 5 | 6 | def broadcast_data(message): 7 | """ Sends a message to all sockets in the connection list. """ 8 | # Send message to everyone, except the server. 9 | for sock in CONNECTION_LIST: 10 | if sock != SERVER_SOCKET: 11 | try: 12 | sock.sendall(message) # send all data at once 13 | except Exception as msg: # Connection was closed. Errors 14 | print(type(msg).__name__) 15 | sock.close() 16 | try: 17 | CONNECTION_LIST.remove(sock) 18 | except ValueError as msg: 19 | print("{}:{}".format(type(msg).__name__, msg)) 20 | 21 | CONNECTION_LIST = [] 22 | RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2 23 | PORT = 1337 24 | 25 | SERVER_SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 26 | SERVER_SOCKET.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 27 | SERVER_SOCKET.bind(("", PORT)) # empty addr string means INADDR_ANY 28 | 29 | print("Listening...") 30 | SERVER_SOCKET.listen(10) # 10 connections 31 | 32 | CONNECTION_LIST.append(SERVER_SOCKET) 33 | print("Server started!") 34 | 35 | while True: 36 | # Get the list sockets which are ready to be read through select 37 | READ_SOCKETS, WRITE_SOCKETS, ERROR_SOCKETS = select.select(CONNECTION_LIST, [], []) 38 | for SOCK in READ_SOCKETS: # New connection 39 | # Handle the case in which there is a new connection recieved through server_socket 40 | if SOCK == SERVER_SOCKET: 41 | SOCKFD, ADDR = SERVER_SOCKET.accept() 42 | CONNECTION_LIST.append(SOCKFD) # add socket descriptor 43 | # Adding \r to prevent message overlapping when another user 44 | # types it's message. 45 | print("\rClient ({0}, {1}) connected".format(ADDR[0], ADDR[1])) 46 | broadcast_data("Client ({0}:{1}) entered room\n" 47 | .format(ADDR[0], ADDR[1]).encode()) 48 | else: # Some incoming message from a client 49 | try: # Data recieved from client, process it 50 | DATA = SOCK.recv(RECV_BUFFER) 51 | if DATA: 52 | ADDR = SOCK.getpeername() # get remote address of the socket 53 | message = "\r[{}:{}]: {}".format(ADDR[0], ADDR[1], DATA.decode()) 54 | print(message, end="") 55 | broadcast_data(message.encode()) 56 | except Exception as msg: # Errors happened, client disconnected 57 | print(type(msg).__name__, msg) 58 | print("\rClient ({0}, {1}) disconnected.".format(ADDR[0], ADDR[1])) 59 | broadcast_data("\rClient ({0}, {1}) is offline\n" 60 | .format(ADDR[0], ADDR[1]).encode()) 61 | SOCK.close() 62 | try: 63 | CONNECTION_LIST.remove(SOCK) 64 | except ValueError as msg: 65 | print("{}:{}.".format(type(msg).__name__, msg)) 66 | continue 67 | 68 | SERVER_SOCKET.close() 69 | -------------------------------------------------------------------------------- /tiny_rmi/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import json 3 | 4 | import select 5 | 6 | 7 | class Server: 8 | __ip = None 9 | __port = None 10 | __listening = False 11 | __server_socket = None 12 | __connection_list = [] 13 | __functions = {} 14 | 15 | def __init__(self, **kwargs): 16 | self.__ip = kwargs["ip"] 17 | self.__port = kwargs["port"] 18 | 19 | self.__server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 20 | self.__server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 21 | self.__server_socket.bind((self.__ip, self.__port)) 22 | 23 | def register_function(self, name, pointer): 24 | self.__functions[name] = pointer 25 | 26 | def listen(self): 27 | self.__listening = True 28 | self.__server_socket.listen(1) 29 | self.__connection_list.append(self.__server_socket) 30 | while self.__listening: 31 | READ_SOCKETS, WRITE_SOCKETS, ERROR_SOCKETS = select.select(self.__connection_list, [], []) 32 | for sock in READ_SOCKETS: # New connection: 33 | # Handle the case in which there is a new connection recieved through server_socket 34 | if sock == self.__server_socket: 35 | fd, addr = self.__server_socket.accept() 36 | self.__connection_list.append(fd) # add socket descriptor 37 | else: # Some incoming message from a client 38 | try: # Data recieved from client, process it 39 | data = sock.recv(4096) 40 | if data: 41 | try: 42 | json_object = json.loads(data.decode()) 43 | print("DEBUG", json_object) 44 | method_name = json_object.get("method", None) 45 | 46 | if not method_name: 47 | raise Exception("Invalid method name!") 48 | 49 | params = json_object.get("parameters", {}) 50 | 51 | ret = self.__functions[method_name](**params) 52 | data_to_send = { 53 | "return": ret 54 | } 55 | sock.sendall(json.dumps(data_to_send).encode()) 56 | except Exception as e: 57 | data_to_send = { 58 | "error": True, 59 | "message": str(e) 60 | } 61 | sock.sendall(json.dumps(data_to_send).encode()) 62 | 63 | except Exception as msg: 64 | sock.close() 65 | try: 66 | self.__connection_list.remove(sock) 67 | except ValueError as msg: 68 | print("{}:{}.".format(type(msg).__name__, msg)) 69 | continue 70 | 71 | 72 | server_singleton = Server(ip="", port=6666) 73 | 74 | 75 | def remote_method(func): 76 | server_singleton.register_function(func.__name__, func) 77 | 78 | def wrapper(*args): 79 | func_result = func(*args) 80 | return func_result 81 | 82 | return wrapper 83 | 84 | 85 | # dummy code 86 | 87 | GLOBAL_DUMMY = 0 88 | 89 | 90 | @remote_method 91 | def set_global_dummy(value): 92 | global GLOBAL_DUMMY 93 | GLOBAL_DUMMY = value 94 | return True 95 | 96 | 97 | @remote_method 98 | def get_global_dummy(): 99 | global GLOBAL_DUMMY 100 | return GLOBAL_DUMMY 101 | 102 | 103 | def main(): 104 | server_singleton.listen() 105 | 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /hangman/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | """ A simple TCP server for playing Hagman """ 3 | import socket 4 | import select 5 | import hangman 6 | 7 | CONNECTION_LIST = [] 8 | SERVER_SOCKET = "" 9 | 10 | def broadcast_all_encode(message): 11 | """ Encodes the message then it broadcasts it to everyone. """ 12 | broadcast_all(message.encode()) 13 | 14 | def broadcast_all(message): 15 | """ Sends a message to all sockets in the connection list. """ 16 | # Send message to everyone, except the server. 17 | for sock in CONNECTION_LIST: 18 | if sock != SERVER_SOCKET: 19 | try: 20 | sock.sendall(message) # send all data at once 21 | except Exception as msg: # Connection was closed. Errors 22 | print(type(msg).__name__, msg) 23 | sock.close() 24 | try: 25 | CONNECTION_LIST.remove(sock) 26 | except ValueError as msg: 27 | print("{}:{}".format(type(msg).__name__, msg)) 28 | 29 | def broadcast_to(client, message): 30 | """ Broadcast a message to a specific client """ 31 | try: 32 | client.sendall(message) # send all data at once 33 | except Exception as msg: # Connection was closed. Errors 34 | print(type(msg).__name__, msg) 35 | client.close() 36 | try: 37 | CONNECTION_LIST.remove(client) 38 | except ValueError as msg: 39 | print(type(msg).__name__, msg) 40 | 41 | RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2 42 | PORT = 1337 43 | 44 | SERVER_SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 45 | SERVER_SOCKET.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 46 | SERVER_SOCKET.bind(("", PORT)) # empty addr string means INADDR_ANY 47 | print("Listening...") 48 | SERVER_SOCKET.listen(10) # 10 connections 49 | 50 | CONNECTION_LIST.append(SERVER_SOCKET) 51 | print("Server started!") 52 | 53 | game = hangman.Hangman(broadcast_all_encode) 54 | while True: 55 | # Get the list sockets which are ready to be read through select 56 | READ_SOCKETS, WRITE_SOCKETS, ERROR_SOCKETS = select.select(CONNECTION_LIST, [], []) 57 | for SOCK in READ_SOCKETS: # New connection 58 | # Handle the case in which there is a new connection recieved through server_socket 59 | if SOCK == SERVER_SOCKET: 60 | SOCKFD, ADDR = SERVER_SOCKET.accept() 61 | CONNECTION_LIST.append(SOCKFD) # add socket descriptor 62 | # Adding \r to prevent message overlapping when another user 63 | # types it's message. 64 | print("\rClient ({0}, {1}) connected".format(ADDR[0], ADDR[1])) 65 | broadcast_all("Client ({0}:{1}) entered room\n" 66 | .format(ADDR[0], ADDR[1]).encode()) 67 | broadcast_to(SOCKFD, "\nWelcome to interactive hangman!\n".encode()) 68 | broadcast_to(SOCKFD, "Append / in front of the message to chat!\n\n".encode()) 69 | game.announce() 70 | else: # Some incoming message from a client 71 | try: # Data recieved from client, process it 72 | DATA = SOCK.recv(RECV_BUFFER) 73 | if DATA: 74 | if game.game_status != 0: 75 | game.new_game() 76 | game.announce() 77 | continue 78 | if DATA.decode()[0] != "/": 79 | game.make_guess(DATA.decode()[0]) 80 | game.announce() 81 | ADDR = SOCK.getpeername() # get remote address of the socket 82 | message = "\r[{}]: {}".format(ADDR[0], DATA.decode()) 83 | print(message, end="") 84 | broadcast_all(message.encode()) 85 | # Handle game 86 | except Exception as msg: # Errors happened, client disconnected 87 | print(type(msg).__name__, msg) 88 | print("\rClient ({0}, {1}) disconnected.".format(ADDR[0], ADDR[1])) 89 | broadcast_all("\rClient ({0}, {1}) is offline\n" 90 | .format(ADDR[0], ADDR[1]).encode()) 91 | SOCK.close() 92 | try: 93 | CONNECTION_LIST.remove(SOCK) 94 | except ValueError as msg: 95 | print("{}:{}.".format(type(msg).__name__, msg)) 96 | continue 97 | 98 | SERVER_SOCKET.close() 99 | -------------------------------------------------------------------------------- /hangman/hangman.py: -------------------------------------------------------------------------------- 1 | #! usr/bin/python3 2 | "A simple Hangman program" 3 | import string 4 | import random 5 | import csv 6 | 7 | # TODO Add score perhaps? 8 | 9 | class Hangman: 10 | """ A simple Hangman class for playing the hangman game. """ 11 | words = [] 12 | 13 | def __init__(self, broadcast_function=print): 14 | self._letter = "" # guessed letter, used internally 15 | self.broadcast = broadcast_function 16 | try: 17 | self._load_words() 18 | self.word = random.choice(self.words) 19 | except IndexError: 20 | print("Error: Cannot make choices!\n") 21 | self.word = "Error" 22 | except FileNotFoundError: 23 | print("Error: File words.csv not found!\n") 24 | self.word = "Error" 25 | 26 | self.original_world = self.word 27 | self.obscured_word = ["*" for i in self.word] 28 | self.guesses_left = len(self.word.replace(" ", "")) # remove spaces 29 | self.letters = [] # Already guessed letters 30 | self.lives = 9 31 | self.game_status = 0 # 0 - on going, 1 - won, 2 - lost 32 | 33 | def _load_words(self): 34 | try: 35 | with open("words.csv", newline="") as csvfile: 36 | reader = csv.reader(csvfile, delimiter=',') 37 | for row in reader: 38 | self.words += row 39 | except FileNotFoundError: 40 | raise FileNotFoundError 41 | 42 | def _obscured_word_str(self): 43 | """ Transforms obscured_word arrat into a string. """ 44 | return "".join(i for i in self.obscured_word) 45 | 46 | def _letter_is_in_word(self): 47 | """Returns the index position of the guess from the word""" 48 | return self.word.find(self._letter) 49 | 50 | def _modify_word(self): 51 | self.word = self.word.replace(self._letter, "_") 52 | 53 | def _modify_obscured_word(self, starting_pos=0): 54 | """ Modifies the obscured world, by revealing guessed letters """ 55 | index = self.word.find(self._letter, starting_pos) 56 | if index >= 0: 57 | self.obscured_word[index] = self._letter 58 | self.guesses_left -= 1 59 | self._modify_obscured_word(index + 1) 60 | 61 | def _bad_guess(self): 62 | message = "{} is not in the word. :(\n".format(self._letter) 63 | self.broadcast(message) 64 | self.lives -= 1 65 | self.update_game_status() 66 | 67 | def _good_guess(self): 68 | message = "You guessed right!\n" 69 | self.broadcast(message) 70 | self._modify_obscured_word() 71 | self._modify_word() 72 | self.update_game_status() 73 | 74 | def _invalid_guess(self): 75 | message = """You need to chose a letter. {} is not a letter! 76 | You lost a life.\n""".format(self._letter) 77 | self.broadcast(message) 78 | self.lives -= 1 79 | self.update_game_status() 80 | 81 | def _perfom_guess(self): 82 | """ Performs the guess by validating the guessed letter. """ 83 | if self._letter in string.ascii_letters: 84 | self.letters.append(self._letter) 85 | if self._letter_is_in_word() >= 0: 86 | self._good_guess() 87 | else: 88 | self._bad_guess() 89 | else: 90 | self._invalid_guess() 91 | 92 | def new_game(self): 93 | """ Starts a new game. """ 94 | message = "Starting new Hangman game!\n" 95 | print("Staring new game!") 96 | self.broadcast(message) 97 | self.__init__(self.broadcast) 98 | 99 | def victory(self): 100 | """ Prints victory message and sets game status to win """ 101 | message = """Congratulations! You won the game! 102 | The word was: {}\n""".format(self.original_world) 103 | print("Users achieved victory!") 104 | self.broadcast(message) 105 | 106 | def defeat(self): 107 | """ Prints defeat message and sets game status to lost """ 108 | message = """Congratulations! You lost the game! 109 | The word was: {}\n""".format(self.original_world) 110 | print("Users lost!") 111 | self.broadcast(message) 112 | 113 | def game_over(self): 114 | if self.game_status == 1: 115 | self.victory() 116 | elif self.game_status == 2: 117 | self.defeat() 118 | 119 | def get_game_status(self): 120 | """ Returns the game status. 1 - won, 2 - lost, 0 on going""" 121 | return self.game_status 122 | 123 | def update_game_status(self): 124 | """ Updates the game status. """ 125 | if self.guesses_left == 0: 126 | self.game_status = 1 127 | elif self.lives == 0: 128 | self.game_status = 2 129 | 130 | def announce(self): 131 | """ Announces the word and lives left or if the game has ended. """ 132 | if self.game_status == 1: 133 | self.victory() 134 | elif self.game_status == 2: 135 | self.defeat() 136 | else: 137 | message = "{} - You have {} lives left.\n".format(self._obscured_word_str(), self.lives) 138 | print(message) 139 | self.broadcast(message) 140 | 141 | def make_guess(self, letter): 142 | """ Makes the guess """ 143 | self._letter = letter.lower() 144 | if self._letter in self.letters: 145 | message = "You already said {}\n".format(self._letter) 146 | self.broadcast(message) 147 | else: 148 | self._perfom_guess() 149 | --------------------------------------------------------------------------------