找回密码
 立即注册
首页 业界区 安全 [Python]基于本地局域网的聊天程序

[Python]基于本地局域网的聊天程序

笙芝 2025-5-31 23:33:57
摘要:基于本地局域网的聊天程序源码
源码

使用python编写,运用 socket和tkinter等模块实现多人在线聊天,拥有基础GUI界面
  1. import socket
  2. import threading
  3. import sys
  4. import time
  5. import tkinter as tk
  6. from tkinter import scrolledtext, messagebox
  7. class ChatServer:
  8.     def __init__(self, host='0.0.0.0', port=12345):
  9.         self.host = host
  10.         self.port = port
  11.         self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  12.         self.server.bind((host, port))
  13.         self.server.listen(5)
  14.         self.clients = []
  15.         self.nicknames = []
  16.         self.heartbeat_interval = 30  # 心跳检测间隔(秒)
  17.         self.last_heartbeat = {}
  18.     def broadcast(self, message):
  19.         for client in self.clients:
  20.             client.send(message)
  21.     def handle(self, client):
  22.         index = self.clients.index(client)
  23.         nickname = self.nicknames[index]
  24.         self.last_heartbeat[client] = time.time()
  25.         
  26.         while True:
  27.             try:
  28.                 message = client.recv(1024)
  29.                 if message == b'HEARTBEAT':
  30.                     self.last_heartbeat[client] = time.time()
  31.                     continue
  32.                 self.broadcast(message)
  33.             except:
  34.                 index = self.clients.index(client)
  35.                 self.clients.remove(client)
  36.                 client.close()
  37.                 nickname = self.nicknames[index]
  38.                 self.broadcast(f'{nickname} 离开了聊天室!'.encode('utf-8'))
  39.                 self.nicknames.remove(nickname)
  40.                 if client in self.last_heartbeat:
  41.                     del self.last_heartbeat[client]
  42.                 break
  43.     def receive(self):
  44.         print('服务器已启动,等待连接...')
  45.         while True:
  46.             try:
  47.                 client, address = self.server.accept()
  48.                 print(f'已连接: {str(address)}')
  49.             except socket.error as e:
  50.                 print(f'接受连接时出错: {e}')
  51.                 continue
  52.             try:
  53.                 client.send('NICK'.encode('utf-8'))
  54.                 nickname = client.recv(1024).decode('utf-8')
  55.                 if not nickname:
  56.                     raise ConnectionError('Empty nickname received')
  57.                 self.nicknames.append(nickname)
  58.                 self.clients.append(client)
  59.             except (ConnectionError, socket.error) as e:
  60.                 print(f'客户端连接异常: {e}')
  61.                 client.close()
  62.                 continue
  63.             print(f'昵称是: {nickname}')
  64.             self.broadcast(f'{nickname} 加入了聊天室!'.encode('utf-8'))
  65.             client.send('已连接到服务器!'.encode('utf-8'))
  66.             thread = threading.Thread(target=self.handle, args=(client,))
  67.             thread.start()
  68. class ChatClientGUI(tk.Tk):
  69.     def __init__(self, host='127.0.0.1', port=12345):
  70.         super().__init__()
  71.         self.title("聊天室客户端")
  72.         self.geometry("600x800")
  73.         self.configure(bg='#f0f0f0')
  74.         
  75.         # 昵称输入
  76.         self.nickname_frame = tk.Frame(self, bg='#f0f0f0')
  77.         self.nickname_frame.pack(pady=10)
  78.         
  79.         tk.Label(self.nickname_frame, text="输入昵称:", bg='#f0f0f0').pack(side=tk.LEFT)
  80.         self.nickname_entry = tk.Entry(self.nickname_frame, width=30)
  81.         self.nickname_entry.pack(side=tk.LEFT, padx=5)
  82.         tk.Button(self.nickname_frame, text="连接", command=self.connect_server).pack(side=tk.LEFT)
  83.         
  84.         # 聊天显示区
  85.         self.chat_frame = tk.Frame(self)
  86.         self.chat_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
  87.         
  88.         self.chat_area = scrolledtext.ScrolledText(self.chat_frame, wrap=tk.WORD, state='disabled')
  89.         self.chat_area.pack(fill=tk.BOTH, expand=True)
  90.         
  91.         # 消息输入区
  92.         self.input_frame = tk.Frame(self, bg='#f0f0f0')
  93.         self.input_frame.pack(pady=10, padx=10, fill=tk.X)
  94.         
  95.         self.message_entry = tk.Entry(self.input_frame, width=50)
  96.         self.message_entry.config(font=('Microsoft YaHei', 10))  # 设置支持中文的字体
  97.         self.message_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
  98.         self.message_entry.bind('<Return>', self.send_message)
  99.         self.message_entry.bind('<Key>', lambda e: 'break' if e.keysym == 'Escape' else None)  # 防止ESC键关闭输入法
  100.         
  101.         tk.Button(self.input_frame, text="发送", command=self.send_message).pack(side=tk.LEFT, padx=5)
  102.         
  103.         # 网络连接
  104.         self.client = None
  105.         self.host = host
  106.         self.port = port
  107.         self.nickname = ""
  108.         
  109.     def connect_server(self):
  110.         self.nickname = self.nickname_entry.get().strip()
  111.         if not self.nickname:
  112.             messagebox.showerror("错误", "请输入昵称")
  113.             return
  114.             
  115.         try:
  116.             self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  117.             self.client.connect((self.host, self.port))
  118.             
  119.             # 发送昵称
  120.             self.client.send(self.nickname.encode('utf-8'))
  121.             
  122.             # 启动接收线程
  123.             receive_thread = threading.Thread(target=self.receive, daemon=True)
  124.             receive_thread.start()
  125.             
  126.             # 禁用昵称输入
  127.             self.nickname_entry.config(state='disabled')
  128.             self.nickname_frame.children['!button'].config(state='disabled')
  129.             
  130.             # 启用消息输入
  131.             self.message_entry.config(state='normal')
  132.             
  133.         except ConnectionRefusedError:
  134.             messagebox.showerror("错误", "服务器未运行")
  135.         except socket.error as e:
  136.             messagebox.showerror("错误", f"连接错误: {e}")
  137.    
  138.     def receive(self):
  139.         while True:
  140.             try:
  141.                 message = self.client.recv(1024).decode('utf-8')
  142.                 self.display_message(message)
  143.             except ConnectionResetError:
  144.                 self.display_message("服务器连接已断开")
  145.                 self.client.close()
  146.                 break
  147.             except socket.error as e:
  148.                 self.display_message(f"网络错误: {e}")
  149.                 self.client.close()
  150.                 break
  151.    
  152.     def send_message(self, event=None):
  153.         message = self.message_entry.get()
  154.         if message and self.client:
  155.             try:
  156.                 full_message = f'{self.nickname}: {message}'
  157.                 self.client.send(full_message.encode('utf-8'))
  158.                 self.message_entry.delete(0, tk.END)
  159.             except UnicodeEncodeError:
  160.                 messagebox.showerror("编码错误", "无法发送包含特殊字符的消息")
  161.             except socket.error as e:
  162.                 messagebox.showerror("网络错误", f"发送失败: {e}")
  163.    
  164.     def display_message(self, message):
  165.         self.chat_area.config(state='normal')
  166.         self.chat_area.insert(tk.END, message + '\n')
  167.         self.chat_area.config(state='disabled')
  168.         self.chat_area.see(tk.END)
  169. class ChatClient:
  170.     def __init__(self, host='127.0.0.1', port=12345):
  171.         self.nickname = input('输入你的昵称: ')
  172.         self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  173.         try:
  174.             self.client.connect((host, port))
  175.         except ConnectionRefusedError:
  176.             print('服务器未运行,请先启动服务器')
  177.             sys.exit(1)
  178.         except socket.error as e:
  179.             print(f'连接错误: {e}')
  180.             sys.exit(1)
  181.     def receive(self):
  182.         while True:
  183.             try:
  184.                 message = self.client.recv(1024).decode('utf-8')
  185.                 if message == 'NICK':
  186.                     self.client.send(self.nickname.encode('utf-8'))
  187.                 else:
  188.                     print(message)
  189.             except ConnectionResetError:
  190.                 print('服务器连接已断开')
  191.                 self.client.close()
  192.                 break
  193.             except socket.error as e:
  194.                 print(f'网络错误: {e}')
  195.                 self.client.close()
  196.                 break
  197.     def write(self):
  198.         while True:
  199.             message = f'{self.nickname}: {input("")}'
  200.             self.client.send(message.encode('utf-8'))
  201. def check_server_running(host='127.0.0.1', port=12345):
  202.     try:
  203.         test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  204.         test_socket.settimeout(1)
  205.         test_socket.connect((host, port))
  206.         test_socket.close()
  207.         return True
  208.     except:
  209.         return False
  210. def find_available_port(start_port=12345, max_tries=100):
  211.     for port in range(start_port, start_port + max_tries):
  212.         try:
  213.             test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  214.             test_socket.bind(('127.0.0.1', port))
  215.             test_socket.close()
  216.             return port
  217.         except:
  218.             continue
  219.     return None
  220. if __name__ == '__main__':
  221.     if check_server_running():
  222.         port = find_available_port()
  223.         if port is None:
  224.             print('无法找到可用端口,请稍后再试')
  225.             sys.exit(1)
  226.         # 使用GUI客户端
  227.         app = ChatClientGUI(port=port)
  228.         app.mainloop()
  229.     else:
  230.         choice = input('未检测到服务器,是否作为服务器启动?(y/n): ')
  231.         if choice.lower() == 'y':
  232.             print('[!]你当前作为服务器启动,可以再次启动程序并使用客户端启动')
  233.             print('[!]请不要关闭此窗口')
  234.             server = ChatServer()
  235.             server.receive()
  236.         else:
  237.             port = find_available_port()
  238.             if port is None:
  239.                 print('无法找到可用端口,请稍后再试')
  240.                 sys.exit(1)
  241.             # 使用GUI客户端
  242.             app = ChatClientGUI(port=port)
  243.             app.mainloop()
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册