-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathP2P_chat.py
More file actions
226 lines (200 loc) · 8.22 KB
/
P2P_chat.py
File metadata and controls
226 lines (200 loc) · 8.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# TEAM - Netech - PROJECT 1 - CS 216.
import socket
import threading
def is_valid_ip(ip):
"""
Validates an IPv4 address.
"""
try:
socket.inet_aton(ip)
return True
except socket.error:
return False
# Global dictionary to store active peers (from incoming messages or sent messages).
active_peers = {}
# Global dictionary to store connected peers (explicit connections).
connected_peers = {}
# Globals to store this peer's listening port and team name
my_listening_port = None
my_team_name = None
def handle_client(conn, addr):
ip, ephemeral_port = addr
try:
while True:
data = conn.recv(1024)
if not data:
break
full_message = data.decode().strip()
# Default values.
sender_listening_port = ephemeral_port
sender_team_name = "unknown_team"
actual_message = full_message
if full_message.startswith("LISTENING_PORT:"):
parts = full_message.split("|", 2)
if len(parts) == 3:
try:
sender_listening_port = int(parts[0].split(":", 1)[1])
except ValueError:
sender_listening_port = ephemeral_port
if parts[1].startswith("TEAM_NAME:"):
sender_team_name = parts[1].split(":", 1)[1]
actual_message = parts[2]
print(f"Received from {ip}:{sender_listening_port} {sender_team_name} -> {actual_message}")
# Update active peers with sender's details.
active_peers[(ip, sender_listening_port)] = True
# If this is a connection message, record it as an explicit connection.
if actual_message.lower() in ["connection message", "manual connection message"]:
connected_peers[(ip, sender_listening_port)] = True
if actual_message.lower() == "exit":
print(f"Peer {ip}:{sender_listening_port} disconnected.")
active_peers.pop((ip, sender_listening_port), None)
connected_peers.pop((ip, sender_listening_port), None)
break
except Exception as e:
print(f"Error handling client {ip}:{sender_listening_port}: {e}")
finally:
conn.close()
def server_thread(my_port):
"""
Starting a TCP server that listens for incoming connections.
For each connection, spawning a new thread to handle it.
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', my_port))
s.listen(5)
while True:
try:
conn, addr = s.accept()
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()
except Exception as e:
print("Server accept error:", e)
break
def send_message():
"""
Upon sending, the peer is added only to active_peers.
"""
global my_listening_port, my_team_name
recipient_ip = input("Enter the recipient's IP address: ").strip()
if not is_valid_ip(recipient_ip):
print("You entered an invalid ip address.")
return
try:
recipient_port = int(input("Enter the recipient's port number: ").strip())
except ValueError:
print("Invalid port number.")
return
message = input("Enter your message: ").strip()
full_message = f"LISTENING_PORT:{my_listening_port}|TEAM_NAME:{my_team_name}|{message}"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((recipient_ip, recipient_port))
s.send(full_message.encode())
print(f"Message sent to {recipient_ip}:{recipient_port}")
# Only add to active_peers here.
active_peers[(recipient_ip, recipient_port)] = True
except Exception as e:
print(f"Connection error: {e}")
finally:
s.close()
def query_peers():
"""
Displays two lists:
1. Active Peers - those who have exchanged messages.
2. Connected Peers - those with which an explicit connection has been established.
"""
print("\n--- Active Peers (from incoming/sent messages) ---")
if active_peers:
for idx, (ip, port) in enumerate(active_peers.keys(), start=1):
print(f"{idx}. {ip}:{port}")
else:
print("No active peers.")
print("\n--- Connected Peers (explicit connections) ---")
if connected_peers:
for idx, (ip, port) in enumerate(connected_peers.keys(), start=1):
print(f"{idx}. {ip}:{port}")
else:
print("No connected peers.")
def connect_to_peers():
"""
Connects to peers.
You can choose to automatically connect to all active peers
or manually enter an IP address and port number to connect.
Upon a successful explicit connection, the peer is added to connected_peers.
"""
global my_listening_port, my_team_name
print("\nChoose connection mode:")
print("A - Automatically connect to all active peers")
print("M - Manually enter IP and port to connect")
mode = input("Enter your choice (A/M): ").strip().upper()
if mode == "A":
if active_peers:
for (ip, port) in list(active_peers.keys()):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
connection_message = f"LISTENING_PORT:{my_listening_port}|TEAM_NAME:{my_team_name}|connection message"
s.send(connection_message.encode())
print(f"Connected to {ip}:{port}")
# Record explicit connection on the sender side.
connected_peers[(ip, port)] = True
except Exception as e:
print(f"Failed to connect to {ip}:{port}: {e}")
finally:
s.close()
else:
print("No active peers to connect.")
elif mode == "M":
manual_ip = input("Enter the IP address to connect to: ").strip()
if not is_valid_ip(manual_ip):
print("You entered an invalid ip address.")
return
try:
manual_port = int(input("Enter the port number to connect to: ").strip())
except ValueError:
print("Invalid port number.")
return
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((manual_ip, manual_port))
connection_message = f"LISTENING_PORT:{my_listening_port}|TEAM_NAME:{my_team_name}|manual connection message"
s.send(connection_message.encode())
print(f"Connected to {manual_ip}:{manual_port}")
connected_peers[(manual_ip, manual_port)] = True
except Exception as e:
print(f"Failed to connect to {manual_ip}:{manual_port}: {e}")
finally:
s.close()
else:
print("Invalid choice. Please try again.")
def main():
global my_listening_port, my_team_name
my_team_name = input("Enter your team name: ").strip()
try:
my_port = int(input("Enter your port number: ").strip())
except ValueError:
print("Invalid port number. Exiting.")
return
my_listening_port = my_port
threading.Thread(target=server_thread, args=(my_port,), daemon=True).start()
print(f"Server listening on port {my_port}")
while True:
print("\n***** Menu *****")
print("1. Send message")
print("2. Query peers")
print("3. Connect to peers (Auto or Manual)")
print("0. Quit")
choice = input("Enter your choice: ").strip()
if choice == "1":
send_message()
elif choice == "2":
query_peers()
elif choice == "3":
connect_to_peers()
elif choice == "0":
print("Exiting...")
break
else:
print("Invalid choice. Please try again.")
if __name__ == "__main__":
main()