├── 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 | 
8 |
9 | 
10 | 
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 
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 | 
33 |
34 | # My Solution To Easter Egg Problem 🥚
35 |
36 | 
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 |
--------------------------------------------------------------------------------