diff --git a/GUI.py b/GUI.py new file mode 100644 index 0000000..fecbfcf --- /dev/null +++ b/GUI.py @@ -0,0 +1,139 @@ +import tkinter as tk +from tkinter import ttk +from datetime import datetime +from p2p import P2PNetwork +import socket + +class ChatUI: + def __init__(self, root): + self.root = root + self.root.title("P2P Segment Chat") + self.root.geometry("800x500") + + self.channel_messages = {} + self.current_channel = "General" + + self.create_channel_list() + self.create_message_display() + self.create_message_input() + self.create_status_bar() + + self.load_channel(self.current_channel) + + self.p2p = P2PNetwork(server_port=5557,on_message_callback=self.receive_message) + self.p2p.start_network() + self.p2p.get_peer_list_from_tracker() + pass + + def create_channel_list(self): + channel_frame = ttk.Frame(self.root, width=200) + channel_frame.pack(side=tk.LEFT, fill=tk.Y) + + ttk.Label(channel_frame, text="Danh sách kênh", font=('Arial', 12, 'bold')).pack(pady=5) + self.channel_list = tk.Listbox(channel_frame) + self.channel_list.pack(fill=tk.BOTH, expand=True, padx=5) + + channels = ["General", "Random", "Project"] + for ch in channels: + self.channel_list.insert(tk.END, ch) + self.channel_messages[ch] = [] + + self.channel_list.bind("<>", self.on_channel_select) + + def create_message_display(self): + msg_frame = ttk.Frame(self.root) + msg_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True) + + self.message_display = tk.Text(msg_frame, state=tk.DISABLED, wrap=tk.WORD) + self.message_display.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + def create_message_input(self): + input_frame = ttk.Frame(self.root) + input_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=5) + + self.message_entry = ttk.Entry(input_frame) + self.message_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(10, 5)) + ttk.Button(input_frame, text="Send", command=self.send_message).pack(side=tk.RIGHT, padx=(5, 10)) + + def create_status_bar(self): + self.status_var = tk.StringVar() + self.status_var.set("Trạng thái: Online") + + status_bar = ttk.Label(self.root, textvariable=self.status_var, anchor="w", relief=tk.SUNKEN) + status_bar.pack(side=tk.BOTTOM, fill=tk.X) + + def send_message(self): + msg = self.message_entry.get() + if msg: + timestamp = datetime.now().strftime("%H:%M:%S") + full_msg = f"[{timestamp}] You: {msg}" + self.channel_messages[self.current_channel].append(full_msg) + self.update_display() + self.message_entry.delete(0, tk.END) + for ip, port in self.p2p.connected_peers: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((ip, port)) + data_to_send = f"{self.current_channel}||{msg}" + self.p2p.send(data_to_send, sock) + sock.close() + except Exception as e: + print(f"[ERROR] Could not send to {(ip, port)}: {e}") + + def receive_message(self, msg): + try: + channel, real_msg = msg.split("||", 1) + except ValueError: + channel, real_msg = "General", msg # fallback nếu định dạng cũ + + timestamp = datetime.now().strftime("%H:%M:%S") + full_msg = f"[{timestamp}] Peer: {real_msg}" + + # Lưu tin nhắn theo kênh + if channel not in self.channel_messages: + self.channel_messages[channel] = [] + + self.channel_messages[channel].append(full_msg) + + # Nếu đang ở đúng kênh thì hiển thị ngay + if channel == self.current_channel: + self.root.after(0, self.update_display) + + + def _append_message(self, msg): + self.channel_messages[self.current_channel].append(msg) + self.update_display() + + def update_display(self): + self.message_display.config(state=tk.NORMAL) + self.message_display.delete("1.0", tk.END) + + for msg in self.channel_messages[self.current_channel]: + self.message_display.insert(tk.END, msg + "\n") + + self.message_display.config(state=tk.DISABLED) + self.message_display.see(tk.END) + + def display_message(self, msg): + self.message_display.config(state=tk.NORMAL) + self.message_display.insert(tk.END, msg + "\n") + self.message_display.config(state=tk.DISABLED) + self.message_display.see(tk.END) + + def on_channel_select(self, event): + selection = self.channel_list.curselection() + if selection: + selected_channel = self.channel_list.get(selection[0]) + if selected_channel != self.current_channel: + self.load_channel(selected_channel) + + def load_channel(self, channel): + self.current_channel = channel + self.status_var.set(f"Currently at channel: {channel}") + self.update_display() + + +if __name__ == "__main__": + root = tk.Tk() + app = ChatUI(root) + root.mainloop() diff --git a/__pycache__/login.cpython-313.pyc b/__pycache__/login.cpython-313.pyc new file mode 100644 index 0000000..c38daa3 Binary files /dev/null and b/__pycache__/login.cpython-313.pyc differ diff --git a/__pycache__/p2p.cpython-313.pyc b/__pycache__/p2p.cpython-313.pyc new file mode 100644 index 0000000..92db30f Binary files /dev/null and b/__pycache__/p2p.cpython-313.pyc differ diff --git a/client.py b/client.py deleted file mode 100644 index d2c06de..0000000 --- a/client.py +++ /dev/null @@ -1,29 +0,0 @@ -import socket -#import threading - -HEADER = 64 -FORMAT = "utf-8" -PORT = 5555 -SERVER = "127.0.0.1" # Replace with the server's IP address -ADDR = (SERVER, PORT) -DIS = "!DISCONNECT" - -client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -client.connect((SERVER, PORT)) - - -def send(msg): - message = msg.encode(FORMAT) - msg_length = len(message) - send_length = str(msg_length).encode(FORMAT) - send_length += b" " * (HEADER - len(send_length)) - client.send(send_length) - client.send(message) - # print(f"Sent: {msg}") - - -send("Hello, server!") -input() -send("This is a test message.") -input() -send(DIS) diff --git a/images/icon.png b/images/icon.png new file mode 100644 index 0000000..836838e Binary files /dev/null and b/images/icon.png differ diff --git a/p2p.py b/p2p.py index 1055e0f..3158efd 100644 --- a/p2p.py +++ b/p2p.py @@ -1,128 +1,144 @@ import socket import threading import time +import json # Thêm import này HEADER = 64 FORMAT = "utf-8" DISCONNECT_MESSAGE = "!DISCONNECT" -TRACKER_HOST = "10.128.242.7" # Localhost for testing +TRACKER_HOST = "192.168.1.102" # Sửa theo IP máy tracker TRACKER_PORT = 5555 -# Ask user which port to use for their server -server_port = int(input("Enter your server port (e.g., 5555): ")) -PEER_HOST = socket.gethostbyname(socket.gethostname()) -ADDR = (PEER_HOST, server_port) - -connected_peers = [] - - -def send(msg, sock): - message = msg.encode(FORMAT) - msg_length = len(message) - send_length = str(msg_length).encode(FORMAT) - send_length += b" " * (HEADER - len(send_length)) - sock.send(send_length) - sock.send(message) - - -# --- REGISTER (TRACKER)--- -def register_with_tracker(): - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client.connect((TRACKER_HOST, TRACKER_PORT)) - - send(f"submit_info {PEER_HOST} {server_port}", client) - print("[REGISTERED] Peer info submitted to tracker.") - - msg_length = client.recv(HEADER).decode(FORMAT) - if msg_length: - msg_length = int(msg_length) - msg = client.recv(msg_length).decode(FORMAT) - print(f"[RECEIVED] Tracker response: {msg}") - time.sleep(2) - - client.close() - - -# --- GET (TRACKER)--- -def get_peer_list_from_tracker(): - global connected_peers - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client.connect((TRACKER_HOST, TRACKER_PORT)) - - send("get_list", client) - print("[REQUEST] Peer list requested from tracker.") - - msg_length = client.recv(HEADER).decode(FORMAT) - if msg_length: - msg_length = int(msg_length) - msg = client.recv(msg_length).decode(FORMAT) - print(f"[RECEIVED] Peer list: {msg}") +def get_local_ip(): + import socket + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + # Kết nối ảo để xác định IP nội mạng + s.connect(("8.8.8.8", 80)) + ip = s.getsockname()[0] + except Exception: + ip = "127.0.0.1" + finally: + s.close() + return ip + + +class P2PNetwork: + def __init__(self, server_port, on_message_callback=None): + self.server_port = server_port + self.PEER_HOST = get_local_ip() + print(f"[DEBUG] My IP: {self.PEER_HOST}") + + self.ADDR = (self.PEER_HOST, self.server_port) + self.connected_peers = [] + self.on_message_callback = on_message_callback + + + def send(self, msg, sock): + message = msg.encode(FORMAT) + msg_length = len(message) + send_length = str(msg_length).encode(FORMAT) + send_length += b" " * (HEADER - len(send_length)) + sock.send(send_length) + sock.send(message) + + def register_with_tracker(self): + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect((TRACKER_HOST, TRACKER_PORT)) + self.send(f"submit_info {self.PEER_HOST} {self.server_port}", client) + print("[REGISTERED] Peer info submitted to tracker.") + + msg_length = client.recv(HEADER).decode(FORMAT) + if msg_length: + msg_length = int(msg_length) + msg = client.recv(msg_length).decode(FORMAT) + print(f"[RECEIVED] Tracker response: {msg}") + time.sleep(1) + client.close() - client.close() + def get_peer_list_from_tracker(self): + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect((TRACKER_HOST, TRACKER_PORT)) + self.send("get_list", client) + print("[REQUEST] Peer list requested from tracker.") -# --- Server Part --- -def handle_peer(conn, addr): - print(f"[INCOMING] Connection from {addr}") - connected = True - while connected: - msg_length = conn.recv(HEADER).decode(FORMAT) + msg_length = client.recv(HEADER).decode(FORMAT) if msg_length: msg_length = int(msg_length) - msg = conn.recv(msg_length).decode(FORMAT) - - if msg == DISCONNECT_MESSAGE: + msg = client.recv(msg_length).decode(FORMAT) + print(f"[RECEIVED] Peer list raw: {msg}") + + try: + peer_data = json.loads(msg) + self.connected_peers = [tuple(p) for p in peer_data if tuple(p) != self.ADDR] + print(f"[PEERS] Updated peer list: {self.connected_peers}") + except Exception as e: + print(f"[ERROR] Failed to parse peer list: {e}") + + client.close() + + def handle_peer(self, conn, addr): + print(f"[INCOMING] Connection from {addr}") + connected = True + while connected: + try: + msg_length = conn.recv(HEADER).decode(FORMAT) + if msg_length: + msg_length = int(msg_length) + msg = conn.recv(msg_length).decode(FORMAT) + + if msg == DISCONNECT_MESSAGE: + connected = False + print(f"[DISCONNECTED] {addr}") + break + + print(f"[{addr}] {msg}") + + # GỌI CALLBACK VỀ GUI TẠI ĐÂY + if self.on_message_callback: + self.on_message_callback(msg) + + + except Exception as e: + print(f"[ERROR] Error handling peer {addr}: {e}") connected = False + conn.close() + + + def start_server(self): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind(self.ADDR) + server.listen() + print(f"[LISTENING] Peer listening on {self.PEER_HOST}:{self.server_port}") + + while True: + conn, addr = server.accept() + thread = threading.Thread(target=self.handle_peer, args=(conn, addr), daemon=True) + thread.start() + + def start_network(self): + self.register_with_tracker() + server_thread = threading.Thread(target=self.start_server, daemon=True) + server_thread.start() + + def manual_connect(self): + target_ip = input("Enter IP of peer to connect to: ") + target_port = int(input("Enter port of peer to connect to: ")) + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect((target_ip, target_port)) + + print("[CONNECTED] Type messages to send. Type '!DISCONNECT' to end.") + while True: + msg = input("> ") + self.send(msg, client) + if msg == DISCONNECT_MESSAGE: + break + elif msg == "get_list": + self.get_peer_list_from_tracker() + elif msg == "add_list": + self.register_with_tracker() - print(f"[{addr}] {msg}") - - conn.close() - print(f"[DISCONNECTED] {addr}") - - -def start_server(): - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server.bind(ADDR) - server.listen() - print(f"[LISTENING] Peer listening on {PEER_HOST}:{server_port}") - - while True: - conn, addr = server.accept() - thread = threading.Thread(target=handle_peer, args=(conn, addr)) - thread.start() - - -# --- Client Part --- -def send_to_peer(): - target_ip = input("Enter IP of peer to connect to: ") - target_port = int(input("Enter port of peer to connect to: ")) - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client.connect((target_ip, target_port)) - - print("[CONNECTED] Type messages to send. Type '!DISCONNECT' to end.") - while True: - msg = input("> ") - send(msg, client) - if msg == DISCONNECT_MESSAGE: - break - elif msg == "get_list": - get_peer_list_from_tracker() - elif msg == "add_list": - register_with_tracker() - - client.close() - - -# --- Main --- -if __name__ == "__main__": - print("[STARTING] Peer is starting...") - register_with_tracker() - - # Start server thread - server_thread = threading.Thread(target=start_server) - server_thread.start() - - # Connect to another peer - client_thread = threading.Thread(target=send_to_peer) - client_thread.start() + client.close() diff --git a/server.py b/server.py deleted file mode 100644 index 9693473..0000000 --- a/server.py +++ /dev/null @@ -1,51 +0,0 @@ -import socket -import threading - -HEADER = 64 -FORMAT = "utf-8" -PORT = 5555 -SERVER = socket.gethostbyname(socket.gethostname()) -ADDR = (SERVER, PORT) -DISCONNECT_MESSAGE = "!DISCONNECT" - -server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -server.bind(ADDR) - -print(socket.gethostname()) -print(SERVER) - - -def handle_client(conn, addr): - print(f"New connection: {addr} connected.") - - connected = True - - while connected: - msg_length = conn.recv(HEADER).decode(FORMAT) - if msg_length: - msg_length = int(msg_length) - msg = conn.recv(msg_length).decode(FORMAT) - - if msg == DISCONNECT_MESSAGE: - connected = False - - print(f"Message from {addr}: {msg}") - - conn.close() - print(f"Connection closed: {addr}") - - -def start(): - server.listen() - print(f"Server is listening on {SERVER}:{PORT}") - - while True: - conn, addr = server.accept() - print(f"New connection: [{addr}]") - thread = threading.Thread(target=handle_client, args=(conn, addr)) - thread.start() - print(f"Active connections: {threading.active_count() - 1}") - - -print("Server is starting...") -start() diff --git a/tracker.py b/tracker.py index f29d7c1..c59929c 100644 --- a/tracker.py +++ b/tracker.py @@ -1,6 +1,7 @@ import socket import threading import argparse +import json # Thêm import này peer_list = [] peer_list_lock = threading.Lock() @@ -36,22 +37,22 @@ def handle_client(addr, conn): print(f"Message from {addr}: {msg}") parts = msg.split() - if parts[0] == "submit_info" and len(parts) == 3: - ip = parts[1] - port = int(parts[2]) - with peer_list_lock: - peer = (ip, port) - if peer not in peer_list: - peer_list.append((ip, port)) - print(f"Peer list updated: {peer_list}") - send(f"Peer {ip}:{port} added to list.", conn) - else: - send(f"Peer {ip}:{port} already in list.", conn) - - elif parts[0] == "get_list": - with peer_list_lock: - print(f"Send Peer list to: {addr}") - send(f"Peer list: {peer_list}", conn) + if parts[0] == "submit_info" and len(parts) == 3: + ip = parts[1] + port = int(parts[2]) + with peer_list_lock: + peer = (ip, port) + if peer not in peer_list: + peer_list.append(peer) + print(f"Peer list updated: {peer_list}") + send(f"Peer {ip}:{port} added to list.", conn) + else: + send(f"Peer {ip}:{port} already in list.", conn) + + elif parts[0] == "get_list": + with peer_list_lock: + print(f"Send Peer list to: {addr}") + send(json.dumps(peer_list), conn) except Exception as e: print(f"Error handling message from {addr}: {e}") @@ -70,6 +71,8 @@ def send(msg, conn): def start(host, port): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind((host, port)) server.listen() print(f"Tracker is running on {SERVER}:{PORT}")