Local Chat App Over LAN (Socket-Based)

 chat_server.py

import socket

import threading


HOST = "0.0.0.0"     # Accept connections from LAN

PORT = 5000


clients = []

usernames = {}


# Broadcast message to all connected clients

def broadcast(msg, sender_conn=None):

    for client in clients:

        if client != sender_conn:

            try:

                client.send(msg.encode())

            except:

                pass


def handle_client(conn, addr):

    print(f"[NEW CONNECTION] {addr}")


    try:

        username = conn.recv(1024).decode()

        usernames[conn] = username


        broadcast(f" {username} joined the chat!")

        print(f"User '{username}' connected.")


        while True:

            msg = conn.recv(1024).decode()

            if not msg:

                break


            full_msg = f"{username}: {msg}"

            print(full_msg)

            broadcast(full_msg, conn)


    except Exception as e:

        print("Error:", e)


    finally:

        print(f"{addr} disconnected")

        clients.remove(conn)

        broadcast(f"{usernames[conn]} left the chat.")

        conn.close()


def start_server():

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server.bind((HOST, PORT))

    server.listen()


    print(f"Server started on {socket.gethostbyname(socket.gethostname())}:{PORT}")


    while True:

        conn, addr = server.accept()

        clients.append(conn)


        thread = threading.Thread(target=handle_client, args=(conn, addr))

        thread.daemon = True

        thread.start()


if __name__ == "__main__":

    start_server()

chat_client.py

import socket
import threading
import tkinter as tk
from tkinter import scrolledtext, messagebox

class ChatClient:
    def __init__(self, root):
        self.root = root
        root.title("LAN Chat Client")
        root.geometry("400x500")

        tk.Label(root, text="Server IP:").pack()
        self.ip_entry = tk.Entry(root)
        self.ip_entry.pack()

        tk.Label(root, text="Username:").pack()
        self.username_entry = tk.Entry(root)
        self.username_entry.pack()

        tk.Button(root, text="Connect", command=self.connect_server).pack(pady=10)

        self.chat_area = scrolledtext.ScrolledText(root, state="disabled")
        self.chat_area.pack(expand=True, fill="both", pady=10)

        self.msg_entry = tk.Entry(root)
        self.msg_entry.pack(fill="x")

        tk.Button(root, text="Send", command=self.send_message).pack(pady=5)

        self.sock = None
        self.running = False

    def connect_server(self):
        ip = self.ip_entry.get()
        username = self.username_entry.get()

        if not ip or not username:
            messagebox.showerror("Error", "Enter IP and Username!")
            return

        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((ip, 5000))
            self.sock.send(username.encode())
        except:
            messagebox.showerror("Error", "Cannot connect to server!")
            return

        self.running = True
        threading.Thread(target=self.receive_messages, daemon=True).start()
        self.chat_area.config(state="normal")
        self.chat_area.insert("end", "Connected!\n")
        self.chat_area.config(state="disabled")

    def receive_messages(self):
        while self.running:
            try:
                msg = self.sock.recv(1024).decode()
                if msg:
                    self.chat_area.config(state="normal")
                    self.chat_area.insert("end", msg + "\n")
                    self.chat_area.yview("end")
                    self.chat_area.config(state="disabled")
            except:
                break

    def send_message(self):
        msg = self.msg_entry.get()
        if msg and self.sock:
            try:
                self.sock.send(msg.encode())
                self.msg_entry.delete(0, tk.END)
            except:
                pass

    def on_close(self):
        self.running = False
        if self.sock:
            self.sock.close()
        self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    client = ChatClient(root)
    root.protocol("WM_DELETE_WINDOW", client.on_close)
    root.mainloop()

No comments: