├── README.md ├── chatlib.py ├── chatlib_tester.py ├── client.py ├── command_server.py ├── command_server_client.py ├── scream_server.py ├── scream_sever_client.py ├── server.py ├── tcp_echo_client.py ├── tcp_echo_server.py ├── udp_client.py ├── udp_server.py ├── unit1_ex1.2.2.py ├── unit1_ex1.3.py ├── unit1_ex1.4.1.py ├── unit2_ex2.2.4.py ├── unit2_ex2.3.3.py ├── unit2_ex2.3.4.py ├── unit2_ex2.3.5.py ├── unit2_ex2.4.4.py ├── unit2_ex2.4.5.py ├── unit3_ex3.3.3.py ├── unit3_ex3.3.4.py ├── unit3_ex3.4.3.py ├── unit4_ex4.2.3.py ├── unit4_ex4.2.4.py └── unit4_ex4.3.4.py /README.md: -------------------------------------------------------------------------------- 1 |

🎓 network.py-python-course 🎓

2 | 3 | 4 | 5 | Introductory course in the field of server and client side programming in python and also network of the cyber education center from campus.gov.il. Includes a final project of a trivia game using a server and a client. 6 | 7 | ![](http://ForTheBadge.com/images/badges/made-with-python.svg) 8 |
9 | ![](https://img.shields.io/tokei/lines/github/lironmiz/network.py-python-course?color=red&label=Lines%20of%20Code) 10 | ![Size](https://img.shields.io/github/repo-size/lironmiz/network.py-python-course?color=red&label=Repo%20Size%20) 11 | 12 |

13 | GitHub top language 14 | 15 | Repository size 16 | 17 | 18 | GitHub last commit 19 | 20 |

21 | 22 | ![NetworkingGIF](https://user-images.githubusercontent.com/91504420/213188528-98db3341-9596-4e30-b073-124bd3c56d46.gif) 23 | 24 | # Course Material 🫡 25 | + Introduction to computer networks 26 | + Programming a TCP socket and creating a client server 27 | + Programming a UDP socket and creating a client server 28 | + A multiplayer TCP server 29 | + A trivia game project used with a server and a client and a protocol built from scratch that summarizes what is learned in the course 30 | 31 | # My Progress Graph In The Course 🌟 32 | ![image](https://user-images.githubusercontent.com/91504420/212348940-9aaa643c-e3af-41ce-94d5-1618cbe52b27.png) 33 | 34 | # My Solution To Easter Egg Problem 🥚 35 | 36 | ![WhatsApp Image 2023-01-13 at 16 43 04](https://user-images.githubusercontent.com/91504420/212350319-cc6637d1-9ed3-40aa-8d9d-8703c35980dc.jpg) 37 | 38 | 39 | -------------------------------------------------------------------------------- /chatlib.py: -------------------------------------------------------------------------------- 1 | # Protocol Constants 2 | 3 | CMD_FIELD_LENGTH = 16 # Exact length of cmd field (in bytes) 4 | LENGTH_FIELD_LENGTH = 4 # Exact length of length field (in bytes) 5 | MAX_DATA_LENGTH = 10**LENGTH_FIELD_LENGTH-1 # Max size of data field according to protocol 6 | MSG_HEADER_LENGTH = CMD_FIELD_LENGTH + 1 + LENGTH_FIELD_LENGTH + 1 # Exact size of header (CMD+LENGTH fields) 7 | MAX_MSG_LENGTH = MSG_HEADER_LENGTH + MAX_DATA_LENGTH # Max size of total message 8 | DELIMITER = "|" # Delimiter character in protocol 9 | DATA_DELIMITER = "#" # Delimiter in the data part of the message 10 | 11 | # Protocol Messages 12 | # In this dictionary we will have all the client and server command names 13 | 14 | PROTOCOL_CLIENT = { 15 | "login_msg": "LOGIN", 16 | "logout_msg": "LOGOUT", 17 | "score_msg": "MY_SCORE", 18 | "highscore_msg": "HIGHSCORE", 19 | "logged_users_msg": "LOGGED", 20 | "get_quest_msg": "GET_QUESTION", 21 | "answer_msg": "SEND_ANSWER" 22 | } 23 | 24 | PROTOCOL_SERVER = { 25 | "login_ok_msg": "LOGIN_OK", 26 | "login_failed_msg": "ERROR", 27 | "score_msg": "YOUR_SCORE", 28 | "highscore_msg": "ALL_SCORE", 29 | "logged_users_list": "LOGGED_ANSWER", 30 | "quest_msg": "YOUR_QUESTION", 31 | "correct_answer_msg": "CORRECT_ANSWER", 32 | "wrong_answer_msg": "WRONG_ANSWER", 33 | "no_quest_msg": "NO_QUESTION", 34 | "error_msg": "ERROR" 35 | } 36 | 37 | ERROR_RETURN = None # What is returned in case of an error 38 | 39 | def build_message(cmd, data): 40 | """ 41 | Gets command name (str) and data field (str) and creates a valid protocol message 42 | Returns: str, or None if error occurred 43 | :type cmd: str 44 | :type data: str 45 | """ 46 | if len(cmd) > CMD_FIELD_LENGTH or len(data) > MAX_DATA_LENGTH: 47 | return None 48 | full_cmd = cmd + " "*(CMD_FIELD_LENGTH-len(cmd)) 49 | data_len = str(len(data)) 50 | full_data_len = "0"*(LENGTH_FIELD_LENGTH-len(data_len))+data_len 51 | full_msg = DELIMITER.join([full_cmd, full_data_len, data]) 52 | return full_msg 53 | 54 | def parse_message(data): 55 | """ 56 | Parses protocol message and returns command name and data field 57 | Returns: cmd (str), data (str). If some error occurred, returns None, None 58 | :type data: str 59 | """ 60 | list_data = data.split("|") 61 | cmd = list_data[0] 62 | if len(list_data) != 3 or len(cmd) != CMD_FIELD_LENGTH: 63 | return None, None 64 | data_len = list_data[1].replace(" ", "") 65 | data_len1 = list_data[1] 66 | if len(data_len1) != LENGTH_FIELD_LENGTH or not data_len.isdigit(): 67 | return None, None 68 | msg = list_data[2] 69 | data_len = int(data_len) 70 | if len(msg) != data_len \ 71 | or not 0 <= data_len <= 9999: 72 | return None, None 73 | cmd = cmd.replace(" ", "") 74 | return cmd, msg 75 | 76 | def split_data(msg, expected_fields): 77 | """ 78 | Helper method. gets a string and number of expected fields in it. Splits the string 79 | using protocol's data field delimiter (|#) and validates that there are correct number of fields. 80 | Returns: list of fields if all ok. If some error occurred, returns None 81 | :type expected_fields: int 82 | :type msg: str 83 | """ 84 | msg_split = msg.split(DATA_DELIMITER) 85 | if len(msg_split) == expected_fields: 86 | return msg_split 87 | return [] 88 | 89 | def join_data(msg_fields): 90 | """ 91 | Helper method. Gets a list, joins all of it's fields to one string divided by the data delimiter. 92 | Returns: string that looks like cell1#cell2#cell3 93 | :type msg_fields: list 94 | """ 95 | for i in range(len(msg_fields)): 96 | msg_fields[i] = str(msg_fields[i]) 97 | return DATA_DELIMITER.join(msg_fields) 98 | -------------------------------------------------------------------------------- /chatlib_tester.py: -------------------------------------------------------------------------------- 1 | import chatlib 2 | 3 | 4 | def check_build(input_cmd, input_data, expected_output): 5 | print("Input: ", input_cmd, input_data, "\nExpected output: ", expected_output) 6 | try: 7 | output = chatlib.build_message(input_cmd, input_data) 8 | except Exception as e: 9 | output = "Exception raised: " + str(e) 10 | 11 | if output == expected_output: 12 | print(".....\t SUCCESS") 13 | else: 14 | print(".....\t FAILED, output: ", output) 15 | 16 | 17 | def check_parse(msg_str, expected_output): 18 | print("Input: ", msg_str, "\nExpected output: ", expected_output) 19 | 20 | try: 21 | output = chatlib.parse_message(msg_str) 22 | except Exception as e: 23 | output = "Exception raised: " + str(e) 24 | 25 | if output == expected_output: 26 | print(".....\t SUCCESS") 27 | else: 28 | print(".....\t FAILED, output: ", output) 29 | 30 | 31 | def main(): 32 | # BUILD 33 | 34 | # Valid inputs 35 | # Normal message 36 | check_build("LOGIN", "aaaa#bbbb", "LOGIN |0009|aaaa#bbbb") 37 | check_build("LOGIN", "aaaabbbb", "LOGIN |0008|aaaabbbb") 38 | # Zero-length message 39 | check_build("LOGIN", "", "LOGIN |0000|") 40 | 41 | # Invalid inputs 42 | # cmd too long 43 | check_build("0123456789ABCDEFG", "", None) 44 | # msg too long 45 | check_build("A", "A" * (chatlib.MAX_DATA_LENGTH + 1), None) 46 | 47 | # PARSE 48 | 49 | # Valid inputs 50 | check_parse("LOGIN | 9|aaaa#bbbb", ("LOGIN", "aaaa#bbbb")) 51 | check_parse(" LOGIN | 9|aaaa#bbbb", ("LOGIN", "aaaa#bbbb")) 52 | check_parse(" LOGIN| 9|aaaa#bbbb", ("LOGIN", "aaaa#bbbb")) 53 | check_parse("LOGIN |9 |aaaa#bbbb", ("LOGIN", "aaaa#bbbb")) 54 | check_parse("LOGIN | 09|aaaa#bbbb", ("LOGIN", "aaaa#bbbb")) 55 | check_parse("LOGIN |0009|aaaa#bbbb", ("LOGIN", "aaaa#bbbb")) 56 | check_parse("LOGIN |9 | aaa#bbbb", ("LOGIN", " aaa#bbbb")) 57 | check_parse("LOGIN | 4|data", ("LOGIN", "data")) 58 | 59 | # Invalid inputs 60 | check_parse("", (None, None)) 61 | check_parse("LOGIN x 4|data", (None, None)) 62 | check_parse("LOGIN | 4xdata", (None, None)) 63 | check_parse("LOGIN | -4|data", (None, None)) 64 | check_parse("LOGIN | z|data", (None, None)) 65 | check_parse("LOGIN | 5|data", (None, None)) 66 | 67 | 68 | if __name__ == '__main__': 69 | main() 70 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import chatlib 3 | import sys 4 | 5 | SERVER_IP = "127.0.0.1" 6 | SERVER_PORT = 5678 7 | 8 | def build_and_send_message(conn, code, data): 9 | """ 10 | Builds a new message using chatlib, wanted code and message. 11 | Prints debug info, then sends it to the given socket. 12 | Paramaters: conn (socket object), code (str), data (str) 13 | Returns: None 14 | """ 15 | full_msg = chatlib.build_message(code, data) 16 | # conn.send(full_msg.encode()) 17 | print("\n*massage sent*\ncommand:", code, "\ndata:", data, "\nsend:", full_msg) 18 | 19 | def recv_message_and_parse(conn): 20 | """ 21 | Recieves a new message from given socket, 22 | then parses the message using chatlib. 23 | Paramaters: conn (socket object) 24 | Returns: cmd (str) and data (str) of the received message. 25 | If error occured, will return None, None 26 | """ 27 | msg = conn.recv(10021).decode() 28 | cmd, data = chatlib.parse_message(msg) 29 | return cmd, data 30 | 31 | def build_send_recv_parse(conn, code, data=""): 32 | """ 33 | function use the sending and receiving functions that i implemented in the previous section one after the other. The function will return 34 | the answer from the server in two strings, msg_code and data. 35 | Paramaters: conn (socket object), code: (str), data: (str) 36 | Returns: msg_code, data 37 | """ 38 | build_and_send_message(conn, code, data) 39 | return recv_message_and_parse(conn) 40 | 41 | def connect(): 42 | """ 43 | Connects to the server 44 | Paramaters: None 45 | Returns: socket object if connected, None otherwise 46 | """ 47 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 48 | client_socket.connect((SERVER_IP, SERVER_PORT)) 49 | return client_socket 50 | 51 | def error_and_exit(error_msg): 52 | """ 53 | Prints an error message and exits the program 54 | Paramaters: error_msg: error message to be printed 55 | Returns: None 56 | """ 57 | print("Error found: \n", error_msg) 58 | sys.exit() 59 | 60 | def login(conn): 61 | """ 62 | Connects to the server using a username and password 63 | Paramaters: conn: socket object 64 | Returns: None 65 | """ 66 | username = input("Please enter username: \n") 67 | password = input("Please enter your password: \n") 68 | login_msg = username+chatlib.DATA_DELIMITER+password 69 | build_and_send_message(conn, chatlib.PROTOCOL_CLIENT["login_msg"], login_msg) 70 | answer = recv_message_and_parse(conn) 71 | while answer[0] != chatlib.PROTOCOL_SERVER["login_ok_msg"]: 72 | print("\nERROR! ") 73 | print(answer[1]) 74 | username = input("Please enter username: \n") 75 | password = input("Please enter your password: \n") 76 | login_msg = username + chatlib.DATA_DELIMITER + password 77 | build_and_send_message(conn, chatlib.PROTOCOL_CLIENT["login_msg"], login_msg) 78 | answer = recv_message_and_parse(conn) 79 | print("logged-in") 80 | 81 | def logout(conn): 82 | """ 83 | Sends a logout command to the server 84 | Paramaters: conn: socket object 85 | Returns: None 86 | """ 87 | build_and_send_message(conn, chatlib.PROTOCOL_CLIENT["logout_msg"], "") 88 | print("goodbye") 89 | 90 | def get_score(conn): 91 | """ 92 | Function which accepts a socket and prints the user's current score. 93 | Paramaters: conn: socket object 94 | Returns: None 95 | """ 96 | ask = chatlib.PROTOCOL_CLIENT["score_msg"] 97 | cmd, data = build_send_recv_parse(conn, ask, "") 98 | if cmd != chatlib.PROTOCOL_SERVER["score_msg"]: 99 | error_and_exit(data) 100 | else: 101 | print("Your score is:", data, "points.") 102 | 103 | def get_highscore(conn): 104 | """ 105 | Gets a socket and prints the Highscores table 106 | Paramaters: conn: socket object 107 | Returns: None 108 | """ 109 | ask = chatlib.PROTOCOL_CLIENT["highscore_msg"] 110 | cmd, data = build_send_recv_parse(conn, ask, "") 111 | if cmd != chatlib.PROTOCOL_SERVER["highscore_msg"]: 112 | error_and_exit(data) 113 | else: 114 | print("\nThe score of all players:\n" + data) 115 | 116 | def connect(): 117 | """ 118 | Connect to a server 119 | Paramaters: None 120 | Returns: conn: socket object 121 | """ 122 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 123 | client_socket.connect((SERVER_IP, SERVER_PORT)) 124 | return client_socket 125 | 126 | def play_question(conn): 127 | """ 128 | The function accepts socket and plays a question 129 | Paramaters: conn: socket object 130 | Returns: None 131 | """ 132 | quest = build_send_recv_parse(conn, chatlib.PROTOCOL_CLIENT["get_quest_msg"]) 133 | if quest[0] == chatlib.PROTOCOL_SERVER["no_quest_msg"]: 134 | print("There is no available questions for you.") 135 | return None 136 | if quest[0] == chatlib.PROTOCOL_SERVER["error_msg"]: 137 | error_and_exit(quest[1]) 138 | elif quest[0] == chatlib.PROTOCOL_SERVER["quest_msg"]: 139 | quest_data = quest[1].split(chatlib.DATA_DELIMITER) 140 | num = quest_data[0] 141 | question = quest_data[1] 142 | answers = [quest_data[2], quest_data[3], quest_data[4], quest_data[5]] 143 | print("\nQ: ", question) 144 | for i in range(1, 5): 145 | print("\t"+str(i)+":\t"+answers[i-1]) 146 | ans_try = input("Choose an answer [1-4]: ") 147 | while ans_try not in ["1", "2", "3", "4"]: 148 | ans_try = input("Enter the number of your choice: ") 149 | score = build_send_recv_parse(conn, chatlib.PROTOCOL_CLIENT["answer_msg"], num+chatlib.DATA_DELIMITER+ans_try) 150 | if score[0] == chatlib.PROTOCOL_SERVER["correct_answer_msg"]: 151 | print("Correct!") 152 | elif score[0] == chatlib.PROTOCOL_SERVER["wrong_answer_msg"]: 153 | print("Wrong... the correct answer is: #"+score[1]) 154 | else: 155 | error_and_exit(score[1]) 156 | 157 | def get_logged_users(conn): 158 | """ 159 | Server accepts a socket and prints the list of all users currently connected to the server. 160 | Paramaters: conn: socket object 161 | Returns: None 162 | """ 163 | logged_users = build_send_recv_parse(conn, chatlib.PROTOCOL_CLIENT["logged_users_msg"]) 164 | if logged_users[0] == chatlib.PROTOCOL_SERVER["logged_users_list"]: 165 | print("Logged_users:\n"+logged_users[1]) 166 | else: 167 | error_and_exit(logged_users[1]) 168 | 169 | def main(): 170 | client_sock = connect() 171 | login(client_sock) 172 | choice = input(""" 173 | p Play a trivia question 174 | s Get my score 175 | h Get highscore 176 | l Get logged users 177 | q Quit 178 | -Enter your choice: """) 179 | while choice != "q": 180 | if choice == "p": 181 | play_question(client_sock) 182 | elif choice == "s": 183 | get_score(client_sock) 184 | elif choice == "h": 185 | get_highscore(client_sock) 186 | elif choice == "l": 187 | get_logged_users(client_sock) 188 | else: 189 | print("Enter the letter of your choice: ") 190 | 191 | choice = input(""" 192 | p Play a trivia question 193 | s Get my score 194 | h Get highscore 195 | l Get logged users 196 | q Quit 197 | -Enter your choice: """) 198 | print("Bye!") 199 | 200 | logout(client_sock) 201 | client_sock.close() 202 | 203 | if __name__ == '__main__': 204 | main() -------------------------------------------------------------------------------- /command_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | import random 4 | 5 | # Create a socket object 6 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | 8 | # Bind the socket to a specific IP and port 9 | s.bind(("127.0.0.1", 1234)) 10 | 11 | # Listen for incoming connectionsNAM 12 | s.listen(1) 13 | 14 | print("Server listening for incoming connections...") 15 | 16 | while True: 17 | # Wait for a connection 18 | c, addr = s.accept() 19 | print("Connection received from", addr) 20 | 21 | # Continue communication as long as the client does not send "Quit" 22 | while True: 23 | # Receive request from the client 24 | request = c.recv(1024).decode() 25 | print("Received request:", request) 26 | 27 | # Check the request and send the appropriate response 28 | if request == "NAME": 29 | response = "Server name: liron_server" 30 | elif request == "TIME": 31 | response = "Current time: " + time.ctime() 32 | elif request == "RAND": 33 | response = "Random number: " + str(random.randint(1, 10)) 34 | elif request == "Quit": 35 | response = "Disconnecting..." 36 | c.send(response.encode()) 37 | c.close() 38 | break 39 | else: 40 | response = "Invalid request" 41 | 42 | # Send the response to the client 43 | c.send(response.encode()) 44 | 45 | if request == "Quit": 46 | break 47 | -------------------------------------------------------------------------------- /command_server_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | # Create a socket object 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | 6 | # Connect to the server 7 | s.connect(("127.0.0.1", 1234)) 8 | 9 | while True: 10 | # Send request to the server 11 | request = input("Enter a request (NAME, TIME, RAND, Quit): ") 12 | s.send(request.encode()) 13 | 14 | # Receive response from the server 15 | response = s.recv(1024).decode() 16 | print("Received response:", response) 17 | 18 | if request == "Quit": 19 | s.close() 20 | break 21 | -------------------------------------------------------------------------------- /scream_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 | 5 | s.bind(("127.0.0.1", 12345)) 6 | 7 | s.listen(1) 8 | 9 | print("Server listening for incoming connections...") 10 | 11 | while True: 12 | c, addr = s.accept() 13 | print("Connection received from", addr) 14 | 15 | message = c.recv(1024).decode() 16 | print("Received message:", message) 17 | 18 | modified_message = message.upper() + "!!!" 19 | 20 | c.send(modified_message.encode()) 21 | c.close() 22 | -------------------------------------------------------------------------------- /scream_sever_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 | 5 | s.connect(("127.0.0.1", 12345)) 6 | 7 | message = input("Enter a message to send to the server: ") 8 | s.send(message.encode()) 9 | 10 | modified_message = s.recv(1024).decode() 11 | print("Received modified message:", modified_message) 12 | 13 | s.close() 14 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | import socket 4 | import chatlib 5 | import select 6 | 7 | # GLOBALS 8 | users = {} 9 | questions = {} 10 | logged_users = {} 11 | messages_to_send = [] 12 | 13 | ERROR_MSG = "Error! " 14 | SERVER_PORT = 5678 15 | SERVER_IP = "127.0.0.1" 16 | 17 | def build_and_send_message(conn, code, msg): 18 | """ 19 | Builds a new message using chatlib, wanted code and message. 20 | Prints debug info, then sends it to the given socket. 21 | Parameters: conn (socket object), code (str), data (str) 22 | Returns: Nothing 23 | """ 24 | full_msg = chatlib.build_message(code, msg) 25 | messages_to_send.append((conn, full_msg.encode())) 26 | conn.send(full_msg.encode()) 27 | print("[SERVER] ", conn.getpeername(), full_msg) # Debug print 28 | 29 | def recv_message_and_parse(conn): 30 | """ 31 | Receives a new message from given socket, 32 | then parses the message using chatlib. 33 | Parameters: conn (socket object) 34 | Returns: cmd (str) and data (str) of the received message. 35 | If error occurred, will return None, None 36 | """ 37 | try: 38 | full_msg = conn.recv(10021).decode() 39 | except ConnectionResetError as error: 40 | print(error) 41 | return None, None 42 | cmd, data = chatlib.parse_message(full_msg) 43 | print("[CLIENT] ", conn.getpeername(), full_msg) # Debug print 44 | return cmd, data 45 | 46 | def print_client_socket(sockets_list): 47 | for sock in sockets_list: 48 | address = sock.getpeername() 49 | print("IP: " + address[0] + ", Port: " + str(address[1])) 50 | 51 | def load_questions(): 52 | """ 53 | Loads questions bank from file to complete 54 | Receives: - 55 | Returns: questions dictionary 56 | """ 57 | global questions 58 | questions = { 59 | 2313: {"question": "How much is 2+2", "answers": ["3", "4", "2", "1"], "correct": 2}, 60 | 4122: {"question": "What is the capital of France?", "answers": ["Lion", "Marseille", "Paris", "Montpelier"], 61 | "correct": 3} 62 | } 63 | 64 | return questions 65 | 66 | def load_user_database(): 67 | """ 68 | Loads users list from file to complete 69 | Receives: - 70 | Returns: user dictionary 71 | """ 72 | global users 73 | users = { 74 | "test": {"password": "test", "score": 0, "questions_asked": []}, 75 | "yossi": {"password": "123", "score": 50, "questions_asked": []}, 76 | "master": {"password": "master", "score": 200, "questions_asked": []} 77 | } 78 | return users 79 | 80 | def setup_socket(): 81 | """ 82 | Creates new listening socket and returns it 83 | Receives: - 84 | Returns: the socket object 85 | """ 86 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 87 | sock.bind((SERVER_IP, SERVER_PORT)) 88 | sock.listen() 89 | print("listening....") 90 | 91 | return sock 92 | 93 | def send_error(conn, error_msg): 94 | """ 95 | Send error message with given message 96 | Receives: socket, message error string from called function 97 | Returns: None 98 | """ 99 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["error_msg"], error_msg) 100 | 101 | def handle_getscore_message(conn, username): 102 | global users 103 | score = users[username]["score"] 104 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["score_msg"], str(score)) 105 | 106 | def handle_logout_message(conn): 107 | """ 108 | Closes the given socket (in later chapters, also remove user from logged_users dictionary) 109 | Receives: socket 110 | Returns: None 111 | """ 112 | global logged_users 113 | user = conn.getpeername() 114 | if user in logged_users: 115 | print("the user: {" + logged_users.pop(user) + "} logout and disconnected") 116 | else: 117 | print("the user disconnected") 118 | conn.close() 119 | 120 | def is_login(conn): 121 | return conn.getpeername() in logged_users 122 | 123 | def handle_login_message(conn, data): 124 | """ 125 | Gets socket and message data of login message. Checks user and pass exists and match. 126 | If not - sends error and finished. If all ok, sends OK message and adds user and address to logged_users 127 | Receives: socket, message code and data 128 | Returns: None (sends answer to client) 129 | """ 130 | global users # needed to access the same users dictionary from all functions 131 | global logged_users # used later 132 | 133 | [username, password] = chatlib.split_data(data, 2) 134 | if username in users: 135 | if users[username]["password"] == password: 136 | if not is_login(conn): 137 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["login_ok_msg"], "") 138 | logged_users[conn.getpeername()] = username 139 | else: 140 | send_error(conn, "this user already login") 141 | else: 142 | send_error(conn, "the password didn't match") 143 | else: 144 | send_error(conn, "the username doesn't exist") 145 | 146 | def handle_highscore_massage(conn): 147 | global users 148 | # sort usernames by score from highest to lowest 149 | highscore = sorted(users, key=lambda u: users[u]["score"], reverse=True) 150 | # build highscore data in string 151 | highscore_data = "" 152 | for user in highscore: 153 | highscore_data = highscore_data + "\t" + user + ":" + str(users[user]["score"]) + "\n" 154 | 155 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["highscore_msg"], highscore_data) 156 | 157 | def handle_client_message(conn, cmd, data): 158 | """ 159 | Gets message code and data and calls the right function to handle command 160 | Receives: socket, message code and data 161 | Returns: None 162 | """ 163 | global logged_users 164 | if cmd == chatlib.PROTOCOL_CLIENT["login_msg"]: 165 | handle_login_message(conn, data) 166 | elif not is_login(conn): 167 | send_error(conn, "command before login") 168 | # elif cmd == chatlib.PROTOCOL_CLIENT["logout_msg"]: 169 | # handle_logout_message(conn) 170 | elif cmd == chatlib.PROTOCOL_CLIENT["score_msg"]: 171 | handle_getscore_message(conn, logged_users[conn.getpeername()]) 172 | elif cmd == chatlib.PROTOCOL_CLIENT["highscore_msg"]: 173 | handle_highscore_massage(conn) 174 | elif cmd == chatlib.PROTOCOL_CLIENT["logged_users_msg"]: 175 | handle_logged_message(conn) 176 | elif cmd == chatlib.PROTOCOL_CLIENT["get_quest_msg"]: 177 | handle_question_massage(conn) 178 | elif cmd == chatlib.PROTOCOL_CLIENT["answer_msg"]: 179 | handle_answer_massage(conn, logged_users[conn.getpeername()], data) 180 | else: 181 | send_error(conn, "command didn't recognized") 182 | 183 | def handle_logged_message(conn): 184 | logged_str = ','.join(logged_users.values()) 185 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["logged_users_list"], logged_str) 186 | 187 | def handle_question_massage(conn): 188 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["quest_msg"], create_random_question()) 189 | 190 | def handle_answer_massage(conn, username, answer_data): 191 | [quest_num, client_answer] = chatlib.split_data(answer_data, 2) 192 | correct_answer = str(questions[int(quest_num)]["correct"]) 193 | if correct_answer == client_answer: 194 | users[username]["score"] += 5 195 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["correct_answer_msg"], "") 196 | else: 197 | build_and_send_message(conn, chatlib.PROTOCOL_SERVER["wrong_answer_msg"], correct_answer) 198 | 199 | def create_random_question(): 200 | quest_num = random.choice(list(questions.keys())) 201 | question = questions[quest_num] 202 | 203 | full_quest_msg = str(quest_num) + chatlib.DATA_DELIMITER + question["question"] + chatlib.DATA_DELIMITER 204 | full_quest_msg += chatlib.DATA_DELIMITER.join(question["answers"]) + chatlib.DATA_DELIMITER + str( 205 | question["correct"]) 206 | 207 | return full_quest_msg 208 | 209 | def main(): 210 | 211 | global users, questions, messages_to_send 212 | 213 | print("Welcome to Trivia Server!") 214 | 215 | load_questions() 216 | load_user_database() 217 | server_sock = setup_socket() 218 | 219 | client_sockets_list = [] 220 | while True: 221 | ready_to_read, ready_to_write, in_error = select.select([server_sock] + client_sockets_list, client_sockets_list, []) 222 | for current_socket in ready_to_read: 223 | if current_socket is server_sock: 224 | (client_socket, client_address) = server_sock.accept() 225 | print("\nnew client connected", client_address) 226 | client_sockets_list.append(client_socket) 227 | print_client_socket(client_sockets_list) 228 | else: 229 | try: 230 | cmd, data = recv_message_and_parse(current_socket) 231 | if cmd is None or cmd == chatlib.PROTOCOL_CLIENT["logout_msg"]: 232 | print("client logout" + str(current_socket.getpeername())) 233 | client_sockets_list.remove(current_socket) 234 | handle_logout_message(current_socket) 235 | elif cmd in chatlib.PROTOCOL_CLIENT.values(): 236 | handle_client_message(current_socket, cmd, data) 237 | else: 238 | send_error(current_socket, "invalid cmd") 239 | print("ERROR: client sent:", cmd, ":", data) 240 | except ConnectionResetError: 241 | client_sockets_list.remove(current_socket) 242 | handle_logout_message(current_socket) 243 | 244 | for out_msg in messages_to_send: 245 | current_socket, data = out_msg 246 | if current_socket in ready_to_write: 247 | try: 248 | current_socket.send(data) 249 | except ConnectionResetError: 250 | print("user disconnected") 251 | handle_logout_message(current_socket) 252 | 253 | if __name__ == '__main__': 254 | main() -------------------------------------------------------------------------------- /tcp_echo_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | MAX_MSG_LENGTH = 1024 3 | SERVER_IP = '127.0.0.1' 4 | SERVER_PORT = 5555 5 | 6 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | my_socket.connect((SERVER_IP, SERVER_PORT)) 8 | while True: 9 | msg = input('Please enter you message\n') 10 | len_msg = len(msg) 11 | my_socket.send(msg.encode()) 12 | if int(len_msg) == 0: 13 | break 14 | data = my_socket.recv(MAX_MSG_LENGTH).decode() 15 | print('The server sent ' + data) 16 | 17 | my_socket.close() 18 | -------------------------------------------------------------------------------- /tcp_echo_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import select 3 | 4 | MAX_MSG_LENGTH = 1024 5 | SERVER_PORT = 5555 6 | SERVER_IP = '0.0.0.0' 7 | 8 | messages_to_send = [] 9 | 10 | """ 11 | This script sets up a server that listens for incoming client connections on IP address '0.0.0.0' and port 5555. 12 | When a new client joins, the server prints a message with the client's address and adds the client socket to a list of connected clients. 13 | The server then continuously listens for data from clients, and if data is received, it prints the data and adds the client socket and data to a list of messages to be sent. 14 | If the data received is empty, the server closes the connection with the client. 15 | Finally, the server sends any messages in the messages_to_send list to the corresponding client sockets. 16 | """ 17 | 18 | def print_client_sockets(client_sockets): 19 | for c in client_sockets: 20 | print("\t", c.getpeername()) 21 | 22 | def main(): 23 | print("Setting up server..." ) 24 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 25 | server_socket.bind((SERVER_IP, SERVER_PORT)) 26 | server_socket.listen() 27 | print("Listening for clients..." ) 28 | client_sockets = [] 29 | while True: 30 | ready_to_read, ready_to_write, in_error = select.select([server_socket] + client_sockets, client_sockets, []) 31 | for current_socket in ready_to_read: 32 | if current_socket is server_socket: 33 | (client_socket, client_address) = current_socket.accept() 34 | print( "New client joined!" ,client_address) 35 | client_sockets.append(client_socket) 36 | print_client_sockets(client_sockets) 37 | else: 38 | try: 39 | data = current_socket.recv(MAX_MSG_LENGTH).decode() 40 | print("New data from client") 41 | if data == "": 42 | print("\tConnection closed from", current_socket.getpeername()) 43 | client_sockets.remove(current_socket) 44 | current_socket.close() 45 | print_client_sockets(client_sockets) 46 | else: 47 | print(data) 48 | messages_to_send.append((current_socket, data)) 49 | except: 50 | print("\t", current_socket.getpeername(), "cloesed the connection") 51 | client_sockets.remove(current_socket) 52 | current_socket.close() 53 | print_client_sockets(client_sockets) 54 | 55 | for message in messages_to_send: 56 | current_socket, data = message 57 | if current_socket in ready_to_write: 58 | current_socket.send(data.encode()) 59 | messages_to_send.remove(message) 60 | 61 | main() -------------------------------------------------------------------------------- /udp_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | # Server IP and port 4 | SERVER_IP = "127.0.0.1" 5 | SERVER_PORT = 8080 6 | 7 | # Create a socket 8 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 9 | 10 | while True: 11 | # Get user input 12 | message = input("Enter a message to send to the server (EXIT to quit): ") 13 | 14 | # Send the message to the server 15 | client_socket.sendto(message.encode(), (SERVER_IP, SERVER_PORT)) 16 | 17 | # Check if the message is "EXIT" 18 | if message == "EXIT": 19 | print("Closing socket...") 20 | client_socket.close() 21 | break 22 | -------------------------------------------------------------------------------- /udp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | SERVER_IP = "127.0.0.1" 4 | SERVER_PORT = 8080 5 | 6 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7 | 8 | server_socket.bind((SERVER_IP, SERVER_PORT)) 9 | 10 | print("Server is listening...") 11 | 12 | while True: 13 | data, client_address = server_socket.recvfrom(1024) 14 | message = data.decode() 15 | print(f"Received message from {client_address}: {message}") 16 | 17 | if message == "EXIT": 18 | print("Closing socket...") 19 | server_socket.close() 20 | break 21 | -------------------------------------------------------------------------------- /unit1_ex1.2.2.py: -------------------------------------------------------------------------------- 1 | # exercise 1.2.2 from unit 1 2 | ''' 3 | Mark all the valid IP addresses: 4 | ''' 5 | 6 | # Answer: 7 | ''' 8 | 1: 1.2.3.4 9 | 2: 0.0.0.0 10 | 3. 216.58.206.68 11 | ''' 12 | -------------------------------------------------------------------------------- /unit1_ex1.3.py: -------------------------------------------------------------------------------- 1 | # exercises 1.3 from unit 1 2 | 3 | #1: 4 | 5 | ''' 6 | Look at the call log on your phone, what kind of information is saved in the call log? 7 | ''' 8 | 9 | # Answer: Only Header 10 | 11 | #2: 12 | 13 | ''' 14 | Teacher Hayuta's 4th grade goes on an annual trip, and the parents must register in the system 15 | the children going on the trip and whether they have medical permission to go on the trip. 16 | Look at the following section copied from the protocol of the system: 17 | Message structure: IDName0U 18 | 19 | Is there a medical certificate (Y) or no medical certificate (N) A string that represents 20 | the name of each registrant. 21 | At the end of the name, add the number 0 (to indicate that this is the end of the string) 22 | . A 3-digit ID number. This number is a unique number for each registrant 23 | (number known in advance). Which of the following messages is written according to the protocol? 24 | 25 | ''' 26 | 27 | # Answer: 101Itay0Y 28 | 29 | #3: 30 | 31 | ''' 32 | True or False: Each server has one port number because it provides only one service. 33 | ''' 34 | 35 | # Answer: False 36 | 37 | -------------------------------------------------------------------------------- /unit1_ex1.4.1.py: -------------------------------------------------------------------------------- 1 | # exercise 1.4.1 from unit 1 2 | ''' 3 | Here is a list of tasks for a reliable protocol in the traffic layer. 4 | Mark the actions performed on the sender's side: 5 | ''' 6 | 7 | # Answer: 8 | 9 | # 1: to add number of each message 10 | 11 | # 2: Send the message to the destination 12 | 13 | # 3: Wait for confirmation from the receiving party that the message has been received 14 | 15 | # 4: Send the following message 16 | 17 | -------------------------------------------------------------------------------- /unit2_ex2.2.4.py: -------------------------------------------------------------------------------- 1 | # exercise 2.2.4 from unit 2 2 | ''' 3 | We are surfing on our laptop in the Chrome browser, and now we would like to watch a video from YouTube. What are the IP and port numbers to which 4 | the browser should go in order to browse the YouTube site? 5 | ''' 6 | 7 | # Answer: The IP address is 216.58.207.46 and the port number is 80 8 | 9 | -------------------------------------------------------------------------------- /unit2_ex2.3.3.py: -------------------------------------------------------------------------------- 1 | # exercise 2.3.3 from unit 2 2 | ''' 3 | You want to establish a TCP socket and send the strings "Hello" to the server whose IP address is 1.2.3.4 and port 80. There is no need to receive information back. Find the bug in the code 4 | 5 | client_tcp.py 6 | import socket 7 | 8 | SERVER_IP = "1.2.3.4" 9 | PORT = 80 10 | 11 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 12 | my_socket.connect((SERVER_IP)) 13 | my_socket.send("hello".encode()) 14 | my_socket.close() 15 | ''' 16 | 17 | # Answer: The server reference does not include a port 18 | 19 | -------------------------------------------------------------------------------- /unit2_ex2.3.4.py: -------------------------------------------------------------------------------- 1 | # exercise 2.3.4 from unit 2 2 | ''' 3 | A client connected to the echo server and sent him the sentence "?May I help you" 4 | In response, he received only the word "May" from the server. 5 | What does the code of that client look like? Complete the missing part 6 | of the plan (highlighted with a marker). 7 | 8 | import socket 9 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | s.connect(("127.0.0.1", 8820)) 11 | s.send("May I help you?".encode()) 12 | 13 | print(_________________.decode()) 14 | 15 | ''' 16 | 17 | # Answer: s.recv(3) 18 | -------------------------------------------------------------------------------- /unit2_ex2.3.5.py: -------------------------------------------------------------------------------- 1 | # exercise 2.3.5 from unit 2 2 | ''' 3 | Ido wrote a client and ran it against the echo server. To his surprise, he got the following error. 4 | 5 | Traceback (most recent call last): 6 | File "C:/idos_client.py", line 5, in 7 | idos_socket.send("hey there") 8 | TypeError: a bytes-like object is required, not 'str' 9 | 10 | ''' 11 | 12 | # Answer: The encode method is not used 13 | -------------------------------------------------------------------------------- /unit2_ex2.4.4.py: -------------------------------------------------------------------------------- 1 | # exercise 2.4.4 from unit 2 2 | ''' 3 | After taking the network.py course, Alice and Bob want to demonstrate the extensive knowledge they have gained in the course, and want to communicate using sockets. 4 | For this purpose, Alice decided that she would open a server, to which Bob would send the messages he wanted to deliver to her. Here is the code that Alice wrote, for creating the server. 5 | Unfortunately, when Alice ran the attached code, she received the following error: 6 | 7 | import socket 8 | 9 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | server_address = ('localhost', 32548) 11 | sock.listen(10) 12 | while True: 13 | print('waiting for a connection') 14 | connection, client_address = sock.accept() 15 | print("received a new connection") 16 | 17 | Traceback (most recent call last): 18 | File "C:/Users/exercises/Server.py", line 8, in 19 | sock.listen(10) 20 | OSError: [WinError 10022] An invalid argument was supplied 21 | 22 | What mistake did Alice make in writing the server? 23 | 24 | ''' 25 | 26 | # Answer: Alice forgot to bind 27 | 28 | -------------------------------------------------------------------------------- /unit2_ex2.4.5.py: -------------------------------------------------------------------------------- 1 | # exercise 2.4.5 from unit 2 2 | ''' 3 | Here is server code (server.py) and client code (client.py). The client wants to send the word hello to the server. 4 | 5 | Server code: 6 | 7 | import socket 8 | 9 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | server_address = ('localhost', 10095) 11 | sock.bind(server_address) 12 | sock.listen(1) 13 | while True: 14 | print('waiting for a connection') 15 | connection, client_address = sock.accept() 16 | print("received a new connection") 17 | 18 | Client code: 19 | 20 | import socket 21 | 22 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 | server_address = ('localhost', 10095) 24 | sock.connect(server_address) 25 | sock.send("hello".encode()) 26 | 27 | The server code runs and then the client code. The server did not receive the hello message, 28 | but the client claims that it did send it. What is the reason? 29 | 30 | ''' 31 | 32 | # Answer: The server did not use the recv method to read 33 | -------------------------------------------------------------------------------- /unit3_ex3.3.3.py: -------------------------------------------------------------------------------- 1 | # exercise 3.3.3 from unit 3 2 | ''' 3 | You are interested in establishing a UDP socket and sending the strings "Hello" to 4 | the server whose IP address is 1.2.3.4 and port 53. There is no need to receive information 5 | back. How will you do this? 6 | ''' 7 | 8 | # Answer: 9 | import socket 10 | 11 | SERVER_IP = "1.2.3.4" 12 | PORT = 53 13 | 14 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 15 | my_socket. sendto( "Hello".encode(), ( SERVER_IP, PORT)) 16 | 17 | my_socket.close() 18 | -------------------------------------------------------------------------------- /unit3_ex3.3.4.py: -------------------------------------------------------------------------------- 1 | # exercise 3.3.4 from unit 3 2 | ''' 3 | Find the bug in the following code: 4 | 5 | import socket 6 | 7 | SERVER_IP = "1.2.3.4" 8 | PORT = 53 9 | 10 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 11 | my_socket.send("Hello".encode(), (SERVER_IP, PORT)) 12 | my_socket.close() 13 | 14 | ''' 15 | 16 | # Answer: Using send and not sendto 17 | -------------------------------------------------------------------------------- /unit3_ex3.4.3.py: -------------------------------------------------------------------------------- 1 | # exercise 3.4.3 from unit 3 2 | ''' 3 | Here is a list of actions. For each operation, choose where it is performed - UDP server 4 | or UDP client. Please note, there are 5 | actions that are performed both on the server and on the client, there are actions 6 | that are performed neither on the server nor on the client. 7 | 8 | ''' 9 | 10 | # Answers: 11 | ''' 12 | 1: bind -> udp server 13 | 14 | 2: listen -> none of them 15 | 16 | 3: socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -> both of them 17 | 18 | 4: accept -> none of them 19 | 20 | 5: connect -> none of them 21 | 22 | 6: send -> none of them 23 | 24 | 7: recv -> none of them 25 | 26 | 8: recvfrom -> both of them 27 | 28 | 9: sendto -> both of them 29 | 30 | 10: close -> both of them 31 | ''' 32 | -------------------------------------------------------------------------------- /unit4_ex4.2.3.py: -------------------------------------------------------------------------------- 1 | # exercise 4.2.3 from unit 4 2 | ''' 3 | Sort from the following list - which lists select accepts and which it returns. 4 | ''' 5 | 6 | # Answers: 7 | 8 | # 1: A list of sockets that we would like to read from - select accepts 9 | 10 | # 2: A list of sockets that we would like to write to - select accepts 11 | 12 | # 3: A list of sockets that can be read from - select return 13 | 14 | #4: A list of sockets that we would like to know if an error occurred in them - select accepts 15 | 16 | -------------------------------------------------------------------------------- /unit4_ex4.2.4.py: -------------------------------------------------------------------------------- 1 | # exercise 4.2.4 from unit 4 2 | ''' 3 | Look at the line of code in front of you 4 | 5 | ready_to_read, ready_to_write, in_error = select.select([server_socket] + client_sockets, [], []) 6 | 7 | Why transfer the server's socket object (server_socket) to the select function? 8 | 9 | ''' 10 | 11 | # Answer: To know which sockets new customers want to connect 12 | 13 | -------------------------------------------------------------------------------- /unit4_ex4.3.4.py: -------------------------------------------------------------------------------- 1 | # exercise 4.3.4 from unit 4 2 | ''' 3 | Here are three sentences. For each of the sentences, determine whether it is true or false. 4 | The sentences are based on the program code that appears in unit 4.3. 5 | 6 | ''' 7 | 8 | # Answers: 9 | 10 | # 1: When the program starts running, the list client_sockets includes only one member - wrong 11 | 12 | # 2: If the ready_to_read list includes a request from an existing client's socket, the server 13 | # should open a connection to it using the accept method - wrong 14 | 15 | # 3: It is not possible to run more than one client on the same computer - wrong 16 | 17 | # 4: The call to the select function in the code is carried out as long as the server is active - true 18 | --------------------------------------------------------------------------------