【Python】开源项目:实时聊天软件

这个软件是一个基于Python的简单聊天客户端和服务器程序,主要用于实现多用户即时聊天功能。它使用了Python的socket库来处理网络通信,以及tkinter库来构建图形用户界面(GUI)。以下是软件的主要功能和特点的详细介绍:

1. 软件功能

1.1 客户端功能
  • 用户登录与身份管理

    • 用户在首次启动客户端时需要输入一个昵称,昵称将被保存到本地文件(identity.txt),下次启动时会自动加载该昵称。

    • 用户可以通过“注销”按钮退出当前账号,同时删除本地保存的昵称文件。

  • 聊天功能

    • 用户可以发送消息到聊天区域,消息会显示在聊天文本框中。

    • 支持群聊功能,所有用户发送的消息都会广播到所有在线用户。

  • 通讯录管理

    • 通讯录区域显示了“世界群”和用户的好友列表。

    • 用户可以通过双击在线用户列表中的用户名称,将该用户添加为好友,好友会自动出现在通讯录中。

    • 如果好友注销,服务器会通知客户端从通讯录中移除该好友。

  • 在线用户管理

    • 用户可以通过点击“全部”按钮,请求服务器获取当前所有在线用户的列表,并显示在在线用户列表区域。

    • 在线用户列表区域支持双击操作,用户可以将在线用户添加为好友。

1.2 服务器功能
  • 用户管理

    • 服务器维护一个在线用户列表,记录所有连接到服务器的用户昵称。

    • 当用户连接时,服务器会提示用户输入昵称,并将昵名加入在线用户列表。

    • 当用户断开连接时,服务器会从在线用户列表中移除该用户,并通知所有客户端。

  • 消息广播

    • 服务器接收来自客户端的消息,并将消息广播到所有连接的客户端。

    • 支持特殊指令,如GET_USERS(获取在线用户列表)、ADD_FRIEND(添加好友)和REMOVE_FRIEND(移除好友)。

2. 软件特点

2.1 界面友好
  • 使用tkinter库构建的图形用户界面,操作简单直观。

  • 界面布局清晰,分为通讯录区域、聊天内容区域和在线用户列表区域,方便用户进行聊天和管理好友。

2.2 实时交互
  • 支持多用户实时聊天,消息即时显示。

  • 在线用户列表和好友列表可以实时更新,用户可以随时了解当前的在线状态。

2.3 简单易用
  • 客户端和服务器的代码简洁明了,易于理解和扩展。

  • 使用Python编写,运行环境要求低,只要有Python环境即可运行。

3. 使用场景

  • 小型团队协作:团队成员可以通过这个聊天工具进行实时沟通,方便项目协作。

  • 学习交流:学生和教师可以使用这个工具进行在线讨论,分享学习心得。

  • 个人社交:用户可以添加好友,进行一对一或群组聊天,满足日常社交需求。

4. 技术实现

4.1 客户端
  • 网络通信:使用socket库建立与服务器的连接,发送和接收消息。

  • 多线程:使用threading库创建一个线程来处理消息接收,避免阻塞主线程。

  • 图形界面:使用tkinter库构建用户界面,包括输入框、按钮、聊天区域和列表框。

4.2 服务器
  • 网络通信:使用socket库监听客户端连接,处理客户端发送的消息。

  • 多线程:为每个客户端连接创建一个线程,处理客户端的请求和消息转发。

  • 日志记录:使用logging库记录服务器运行日志,方便调试和监控。

5. 未来扩展

  • 私密聊天:支持用户之间的一对一私密聊天。

  • 文件传输:允许用户发送和接收文件。

  • 群组管理:支持创建和管理多个群组,用户可以加入或退出群组。

  • 用户认证:增加用户密码登录功能,提高安全性。

6. 代码开源

        客户端.py

import socket
import threading
import tkinter as tk
from tkinter import scrolledtext, messagebox, ttk
from tkinter import simpledialog
import os

class ChatClient:
    def __init__(self, host='127.0.0.1', port=12345):
        self.host = host
        self.port = port
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client_socket.connect((self.host, self.port))

        # 设置窗口
        self.root = tk.Tk()
        self.root.title("Chat Client")
        self.root.geometry("1200x600")  # 调整窗口宽度以容纳新的区域
        self.root.resizable(False, False)
        self.root.configure(bg="#f0f0f0")

        # 设置字体和颜色
        self.font = ("Arial", 12)
        self.bg_color = "#f0f0f0"
        self.input_bg_color = "#ffffff"
        self.button_color = "#4CAF50"
        self.text_color = "#333333"

        # 检查身份文件
        self.identity_file = "identity.txt"
        if os.path.exists(self.identity_file):
            with open(self.identity_file, "r") as file:
                self.nickname = file.read().strip()
            self.client_socket.send(self.nickname.encode('utf-8'))
        else:
            self.nickname = simpledialog.askstring("Nickname", "Choose your nickname:", parent=self.root)
            if not self.nickname:
                messagebox.showerror("Error", "Nickname is required!")
                self.root.destroy()
                return
            self.client_socket.send(self.nickname.encode('utf-8'))
            with open(self.identity_file, "w") as file:
                file.write(self.nickname)

        # 主聊天区域和用户列表区域
        self.main_frame = tk.Frame(self.root, bg=self.bg_color)
        self.main_frame.pack(fill='both', expand=True, padx=20, pady=20)

        # 通讯录区域
        self.contact_frame = tk.Frame(self.main_frame, bg=self.bg_color, width=200)
        self.contact_frame.pack(side='left', fill='both', expand=False, padx=10, pady=10)

        self.contact_label = tk.Label(self.contact_frame, text="通讯录", font=self.font, bg=self.bg_color, fg=self.text_color)
        self.contact_label.pack(fill='x')

        self.contact_list = tk.Listbox(self.contact_frame, font=self.font, bg=self.bg_color, fg=self.text_color)
        self.contact_list.pack(fill='both', expand=True)
        self.contact_list.bind("", self.on_contact_click)

        # 聊天内容区域
        self.chat_frame = tk.Frame(self.main_frame, bg=self.bg_color)
        self.chat_frame.pack(side='left', fill='both', expand=True, padx=10, pady=10)

        self.chat_area = scrolledtext.ScrolledText(self.chat_frame, state='disabled', height=20, width=50,
                                                   font=self.font, bg=self.bg_color, fg=self.text_color)
        self.chat_area.pack(fill='both', expand=True)

        # 在线用户列表区域
        self.user_list_frame = tk.Frame(self.main_frame, bg=self.bg_color, width=200)
        self.user_list_frame.pack(side='right', fill='both', expand=False, padx=10, pady=10)

        self.user_list_label = tk.Label(self.user_list_frame, text="在线用户", font=self.font, bg=self.bg_color, fg=self.text_color)
        self.user_list_label.pack(fill='x')

        self.user_list = tk.Listbox(self.user_list_frame, font=self.font, bg=self.bg_color, fg=self.text_color)
        self.user_list.pack(fill='both', expand=True)
        self.user_list.bind("", self.on_user_click)

        # 输入框和按钮的容器
        self.input_frame = tk.Frame(self.root, bg=self.bg_color)
        self.input_frame.pack(fill='x', padx=20, pady=20)

        # 输入框
        self.entry = tk.Entry(self.input_frame, width=50, font=self.font, bg=self.input_bg_color, fg=self.text_color)
        self.entry.pack(side='left', fill='x', expand=True, padx=10)
        self.entry.focus()

        # 发送按钮
        self.send_button = ttk.Button(self.input_frame, text="Send", command=self.send_message, style="TButton")
        self.send_button.pack(side='left', padx=20)

        # 全部按钮
        self.all_button = ttk.Button(self.input_frame, text="全部", command=self.show_all_users, style="TButton")
        self.all_button.pack(side='left', padx=20)

        # 注销按钮
        self.logout_button = ttk.Button(self.input_frame, text="Logout", command=self.logout, style="TButton")
        self.logout_button.pack(side='left', padx=20)

        # 设置样式
        style = ttk.Style()
        style.configure("TButton", font=self.font, background=self.button_color, foreground="white")

        # 初始化通讯录
        self.contacts = ["世界群"]
        self.update_contact_list()

        # 接收消息的线程
        threading.Thread(target=self.receive_messages, daemon=True).start()

        # 启动主循环
        self.root.mainloop()

    def send_message(self):
        message = self.entry.get()
        if message:
            selected_contact = self.contact_list.get(self.contact_list.curselection())
            self.client_socket.send(f"{self.nickname} to {selected_contact}: {message}".encode('utf-8'))
            self.entry.delete(0, tk.END)
            self.entry.focus()

    def receive_messages(self):
        while True:
            try:
                message = self.client_socket.recv(1024).decode('utf-8')
                if message.startswith("USERS:"):
                    # 更新用户列表
                    users = message.split(":")[1].strip().split(",")
                    self.update_user_list(users)
                elif message.startswith("REMOVE_FRIEND:"):
                    # 移除好友
                    friend = message.split(":")[1].strip()
                    if friend in self.contacts:
                        self.contacts.remove(friend)
                        self.update_contact_list()
                else:
                    # 显示聊天消息
                    self.chat_area.configure(state='normal')
                    self.chat_area.insert(tk.END, message + "\n")
                    self.chat_area.configure(state='disabled')
                    self.chat_area.see(tk.END)
            except Exception as e:
                messagebox.showerror("Error", f"An error occurred: {e}")
                self.client_socket.close()
                break

    def logout(self):
        os.remove(self.identity_file)
        self.root.destroy()

    def show_all_users(self):
        # 请求服务器发送当前所有用户
        self.client_socket.send("GET_USERS".encode('utf-8'))

    def update_user_list(self, users):
        self.user_list.delete(0, tk.END)  # 清空当前用户列表
        for user in users:
            self.user_list.insert(tk.END, user)

    def update_contact_list(self):
        self.contact_list.delete(0, tk.END)  # 清空当前通讯录
        for contact in self.contacts:
            self.contact_list.insert(tk.END, contact)

    def on_contact_click(self, event):
        selection = event.widget.curselection()
        if selection:
            index = selection[0]
            contact = event.widget.get(index)
            self.chat_area.configure(state='normal')
            self.chat_area.delete(1.0, tk.END)
            self.chat_area.insert(tk.END, f"Chatting with: {contact}\n")
            self.chat_area.configure(state='disabled')

    def on_user_click(self, event):
        selection = event.widget.curselection()
        if selection:
            index = selection[0]
            user = event.widget.get(index)
            if user not in self.contacts:
                self.contacts.append(user)
                self.update_contact_list()
                self.client_socket.send(f"ADD_FRIEND:{user}".encode('utf-8'))

if __name__ == "__main__":
    client = ChatClient()

         服务器端.py

import socket
import threading
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class ChatServer:
    def __init__(self, host='127.0.0.1', port=12345):
        self.host = host
        self.port = port
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen()
        logging.info(f"Server started on {self.host}:{self.port}")
        self.clients = []
        self.nicknames = []

    def broadcast(self, message):
        for client in self.clients:
            try:
                client.send(message)
            except:
                self.clients.remove(client)
                client.close()

    def handle_client(self, client):
        while True:
            try:
                message = client.recv(1024)
                if not message:
                    break
                message = message.decode('utf-8')
                logging.info(f"Received message: {message}")
                if message == "GET_USERS":
                    # 发送当前所有用户
                    user_list = ",".join(self.nicknames)
                    client.send(f"USERS:{user_list}".encode('utf-8'))
                elif message.startswith("ADD_FRIEND:"):
                    # 处理添加好友请求
                    friend = message.split(":")[1].strip()
                    self.broadcast(f"ADD_FRIEND:{friend}".encode('utf-8'))
                else:
                    # 广播消息
                    self.broadcast(message.encode('utf-8'))
            except Exception as e:
                logging.error(f"Error handling client: {e}")
                break
        index = self.clients.index(client)
        self.clients.remove(client)
        client.close()
        nickname = self.nicknames[index]
        self.nicknames.remove(nickname)
        self.broadcast(f"REMOVE_FRIEND:{nickname}".encode('utf-8'))
        logging.info(f'{nickname} left the chat!')

    def receive(self):
        while True:
            try:
                client, address = self.server_socket.accept()
                logging.info(f"Connected with {address}")
                client.send("NICK".encode('utf-8'))
                nickname = client.recv(1024).decode('utf-8')
                self.nicknames.append(nickname)
                self.clients.append(client)
                logging.info(f"Nickname of the client is {nickname}")
                self.broadcast(f"{nickname} joined the chat!".encode('utf-8'))
                client.send("Connected to the server!".encode('utf-8'))
                thread = threading.Thread(target=self.handle_client, args=(client,))
                thread.start()
            except Exception as e:
                logging.error(f"Server error occurred: {e}")
                break

if __name__ == "__main__":
    server = ChatServer()
    server.receive()

这个软件是一个很好的学习项目,可以帮助开发者理解网络编程、多线程和图形用户界面开发的基本概念。同时,它也可以作为一个简单的即时通讯工具,用于日常交流和协作。

你可能感兴趣的:(网络,服务器,python,开发语言)