Skip to content

Commit 6de54ab

Browse files
authored
Update Distributed-Chat-Application.py
1 parent eab4b5d commit 6de54ab

1 file changed

Lines changed: 61 additions & 23 deletions

File tree

185-Distributed-chat-application/Distributed-Chat-Application.py

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,27 @@
1111
from ttkbootstrap.widgets.scrolled import ScrolledText
1212

1313
# ================== CONFIG ================== #
14-
HOST = "0.0.0.0" # LAN / WAN support
1514
PORT = 5050
1615
BUFFER_SIZE = 4096
1716
DB_FILE = "chat.db"
17+
PASSWORD = "Secret123" # Server password
18+
19+
# ================== UTILITY: AUTO LAN IP ================== #
20+
def get_local_ip():
21+
"""
22+
Detects LAN IP automatically.
23+
Falls back to 127.0.0.1 if none found.
24+
"""
25+
try:
26+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
27+
s.connect(("8.8.8.8", 80)) # Doesn't actually send packets
28+
ip = s.getsockname()[0]
29+
s.close()
30+
return ip
31+
except Exception:
32+
return "127.0.0.1"
33+
34+
HOST = get_local_ip()
1835

1936
# ================== DATABASE ================== #
2037
def init_db():
@@ -32,7 +49,6 @@ def init_db():
3249
conn.commit()
3350
conn.close()
3451

35-
3652
def save_message(sender, content, reply_to=None):
3753
conn = sqlite3.connect(DB_FILE)
3854
cur = conn.cursor()
@@ -43,7 +59,6 @@ def save_message(sender, content, reply_to=None):
4359
conn.commit()
4460
conn.close()
4561

46-
4762
def load_history(limit=50):
4863
conn = sqlite3.connect(DB_FILE)
4964
cur = conn.cursor()
@@ -59,15 +74,19 @@ def load_history(limit=50):
5974
clients: Dict[socket.socket, str] = {}
6075
server_running = True
6176

62-
63-
def start_server(log_callback):
77+
def start_secure_server(log_callback):
6478
init_db()
6579

6680
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
67-
server.bind((HOST, PORT))
68-
server.listen()
81+
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
82+
try:
83+
server.bind((HOST, PORT))
84+
except Exception as e:
85+
log_callback(f"Error binding server on {HOST}:{PORT}: {e}")
86+
return
6987

70-
log_callback(f"Server running on {HOST}:{PORT}")
88+
server.listen()
89+
log_callback(f"Secure server running on {HOST}:{PORT} (LAN)")
7190

7291
def broadcast(message):
7392
for c in list(clients.keys()):
@@ -78,10 +97,21 @@ def broadcast(message):
7897

7998
def handle_client(conn, addr):
8099
try:
81-
username = conn.recv(BUFFER_SIZE).decode()
100+
# Step 1: Password authentication
101+
conn.send("Enter server password:".encode())
102+
pw = conn.recv(BUFFER_SIZE).decode().strip()
103+
if pw != PASSWORD:
104+
conn.send("❌ Wrong password. Connection closed.".encode())
105+
conn.close()
106+
log_callback(f"Rejected connection from {addr} (wrong password)")
107+
return
108+
109+
# Step 2: Get username
110+
conn.send("Password accepted. Enter your username:".encode())
111+
username = conn.recv(BUFFER_SIZE).decode().strip()
82112
clients[conn] = username
83113

84-
# Send chat history
114+
# Step 3: Send chat history
85115
for sender, content, reply_to, ts in load_history():
86116
prefix = f"[{ts}] {sender}:"
87117
if reply_to:
@@ -92,6 +122,7 @@ def handle_client(conn, addr):
92122
broadcast(f"[SERVER] 🟢 {username} joined the chat")
93123
log_callback(f"{username} connected from {addr}")
94124

125+
# Step 4: Main loop
95126
while server_running:
96127
raw = conn.recv(BUFFER_SIZE).decode()
97128
if not raw:
@@ -115,19 +146,33 @@ def handle_client(conn, addr):
115146
name = clients.pop(conn, "Unknown")
116147
conn.close()
117148
broadcast(f"[SERVER] 🔴 {name} left the chat")
149+
log_callback(f"{name} disconnected from {addr}")
118150

151+
# Accept clients
119152
while True:
120-
conn, addr = server.accept()
121-
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()
153+
try:
154+
conn, addr = server.accept()
155+
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()
156+
except Exception as e:
157+
log_callback(f"Server accept error: {e}")
158+
break
122159

123160
# ================== CLIENT ================== #
124161
class ChatClient:
125162
def __init__(self, username, host, message_callback):
126163
self.username = username
127164
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
128165
self.sock.connect((host, PORT))
129-
self.sock.send(username.encode())
130166
self.message_callback = message_callback
167+
168+
# Step 1: Send password
169+
self.sock.recv(BUFFER_SIZE) # "Enter server password:"
170+
self.sock.send(PASSWORD.encode())
171+
172+
# Step 2: Send username
173+
self.sock.recv(BUFFER_SIZE) # "Password accepted. Enter username:"
174+
self.sock.send(username.encode())
175+
131176
threading.Thread(target=self.listen, daemon=True).start()
132177

133178
def listen(self):
@@ -163,12 +208,11 @@ def send(self, msg, reply_to=None):
163208
user_entry = tb.Entry(login)
164209
user_entry.pack(fill=tk.X, pady=5)
165210

166-
tb.Label(login, text="Server IP (LAN / WAN)").pack()
211+
tb.Label(login, text="Server IP (LAN)").pack()
167212
ip_entry = tb.Entry(login)
168-
ip_entry.insert(0, "127.0.0.1")
213+
ip_entry.insert(0, HOST)
169214
ip_entry.pack(fill=tk.X, pady=5)
170215

171-
172216
def join_chat():
173217
global username, server_ip, client
174218
username = user_entry.get().strip()
@@ -179,7 +223,6 @@ def join_chat():
179223
client = ChatClient(username, server_ip, display_message)
180224
display_message(f"[SYSTEM] Connected as {username}")
181225

182-
183226
tb.Button(login, text="Join Chat", bootstyle="success", command=join_chat).pack(pady=15)
184227

185228
# ---------- CHAT ---------- #
@@ -198,7 +241,6 @@ def join_chat():
198241
msg_entry = tb.Entry(entry_frame)
199242
msg_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
200243

201-
202244
def send_message():
203245
global reply_target
204246
text = msg_entry.get().strip()
@@ -208,30 +250,26 @@ def send_message():
208250
reply_label.config(text="")
209251
msg_entry.delete(0, tk.END)
210252

211-
212253
msg_entry.bind("<Return>", lambda e: send_message())
213254

214-
215255
def on_click(event):
216256
global reply_target
217257
idx = chat_box.text.index("@%s,%s linestart" % (event.x, event.y))
218258
line = chat_box.text.get(idx, idx + " lineend").strip()
219259
reply_target = line
220260
reply_label.config(text=f"Replying to: {line[:50]}...")
221261

222-
223262
def display_message(msg):
224263
chat_box.text.configure(state="normal")
225264
chat_box.text.insert("end", msg + "\n")
226265
chat_box.text.see("end")
227266
chat_box.text.configure(state="disabled")
228267

229-
230268
chat_box.text.bind("<Button-1>", on_click)
231269

232270
tb.Button(entry_frame, text="Send", bootstyle="primary", command=send_message).pack(side=tk.RIGHT)
233271

234272
# ---------- START SERVER THREAD ---------- #
235-
threading.Thread(target=start_server, args=(display_message,), daemon=True).start()
273+
threading.Thread(target=start_secure_server, args=(display_message,), daemon=True).start()
236274

237275
app.mainloop()

0 commit comments

Comments
 (0)