当前位置: 首页 > news >正文

20254106 实验三《Python程序设计》实验报告

20254106 实验三《Python程序设计》实验报告

20254106 2025-2026-2 《Python程序设计》实验三报告

课程:《Python程序设计》
班级: 2541
姓名: 谢林儒
学号:20254106
实验教师:王志强
实验日期:2026年4月28日
必修/选修: 公选课

1.实验内容

创建服务端和客户端,服务端在特定端口监听多个客户请求。客户端和服务端通过Socket套接字(TCP/UDP)进行通信。

2. 实验过程及结果

A.同队友(20254125祁芸萱)使用课程代码进行通信。
(1)修改电脑IP地址,以本人(20254106)的末位学号“6”作为IP结尾;
IP
(2)创建服务端和客户端,选择一个通信端口,用Python语言编程实现通信演示程序;
a.server端编程
import socket #pip install socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
HOST = "172.20.10.6" #"localhost"
PORT = 4444
server.bind((HOST, PORT))
server.listen(1)
print("服务端已经启动,等待客户端连接......")
conn, addr = server.accept()
print(f"已连接客户端:{addr}")
while True:
#接收消息
data = conn.recv(1024).decode("utf-8")
if not data or data == "exit":
print("聊天结束")
break
print(f"客户端:{data}")

send_msg = input("我:")
conn.send(send_msg.encode("utf-8"))
if send_msg == "exit":break

conn.close()
server.close()
b.client端编程
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
HOST = "172.20.10.25"
PORT = 4444
client.connect((HOST, PORT))
print("已连接服务端!输入exit退出聊天!")
while True:
#发送数据
send_msg = input("我:")
client.send(send_msg.encode("utf-8"))
if send_msg == "exit":
break
#接收数据
data = client.recv(1024).decode("utf-8")
if not data or data == "exit":
print("聊天结束")
break
print(f"\n服务端:{data}")

client.close()
(3)要求发送方输入内容,并传输;接收方收到信息并显示。
a.server端视角
server
b.client端视角
client

要求2:使用LLM生成一个带图形界面的程序
(1)使用deepseek专家模式,输入实验相关提示词,进而输出代码;
import tkinter as tk
from tkinter import scrolledtext, messagebox
import socket
import threading
import queue

---------------------------- 服务端模块 ----------------------------

class ServerWindow:
def init(self, root):
self.root = root
self.root.title("聊天室 - 服务端(可参与聊天)")
self.root.geometry("600x550")
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

    self.server_socket = Noneself.is_running = Falseself.client_sockets = []self.client_addresses = {}self.threads = []self.message_queue = queue.Queue()# ------ 顶部控制栏 ------frame_top = tk.Frame(root)frame_top.pack(pady=5, padx=5, fill=tk.X)tk.Label(frame_top, text="监听端口:").pack(side=tk.LEFT)self.port_var = tk.StringVar(value="5000")tk.Entry(frame_top, textvariable=self.port_var, width=8).pack(side=tk.LEFT, padx=5)self.start_btn = tk.Button(frame_top, text="启动服务", command=self.start_server)self.start_btn.pack(side=tk.LEFT, padx=5)self.stop_btn = tk.Button(frame_top, text="停止服务", command=self.stop_server, state=tk.DISABLED)self.stop_btn.pack(side=tk.LEFT, padx=5)# ------ 日志/聊天记录区域 ------self.log_text = scrolledtext.ScrolledText(root, state='normal', wrap=tk.WORD)self.log_text.pack(padx=5, pady=5, fill=tk.BOTH, expand=True)# ------ 在线客户端列表 ------frame_mid = tk.LabelFrame(root, text="在线客户端")frame_mid.pack(padx=5, pady=5, fill=tk.X)self.client_listbox = tk.Listbox(frame_mid, height=4)self.client_listbox.pack(fill=tk.X, padx=5, pady=5)# ------ 服务端消息输入区域 (NEW) ------frame_input = tk.Frame(root)frame_input.pack(padx=5, pady=5, fill=tk.X)self.server_msg_entry = tk.Entry(frame_input, state=tk.DISABLED)self.server_msg_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0,5))self.server_msg_entry.bind("<Return>", lambda e: self.send_server_message())self.server_send_btn = tk.Button(frame_input, text="发送", command=self.send_server_message, state=tk.DISABLED)self.server_send_btn.pack(side=tk.RIGHT)# 定时检查消息队列,更新UIself.update_gui_from_queue()def log(self, msg):"""线程安全地添加日志"""self.message_queue.put(("log", msg))def update_client_list(self):self.message_queue.put(("update_list", None))def set_server_input_state(self, state):self.message_queue.put(("set_input", state))def update_gui_from_queue(self):try:while True:task, data = self.message_queue.get_nowait()if task == "log":self.log_text.insert(tk.END, data + "\n")self.log_text.see(tk.END)elif task == "update_list":self.client_listbox.delete(0, tk.END)for addr in self.client_addresses.values():self.client_listbox.insert(tk.END, f"{addr[0]}:{addr[1]}")elif task == "set_input":self.server_msg_entry.config(state=data)self.server_send_btn.config(state=data)except queue.Empty:passfinally:self.root.after(100, self.update_gui_from_queue)def start_server(self):port_str = self.port_var.get()if not port_str.isdigit():messagebox.showerror("错误", "请输入有效的端口号")returnport = int(port_str)try:self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.server_socket.bind(("0.0.0.0", port))self.server_socket.listen(10)except Exception as e:messagebox.showerror("启动失败", f"无法启动服务器: {e}")returnself.is_running = Trueself.start_btn.config(state=tk.DISABLED)self.stop_btn.config(state=tk.NORMAL)self.set_server_input_state(tk.NORMAL)  # 启用服务端输入self.log(f"服务端已启动,监听端口 {port},你可以在下方输入消息参与聊天")accept_thread = threading.Thread(target=self.accept_clients, daemon=True)accept_thread.start()self.threads.append(accept_thread)def accept_clients(self):while self.is_running:try:client_sock, addr = self.server_socket.accept()self.client_sockets.append(client_sock)self.client_addresses[client_sock] = addrself.log(f"新连接来自 {addr[0]}:{addr[1]}")self.update_client_list()t = threading.Thread(target=self.handle_client, args=(client_sock,), daemon=True)t.start()self.threads.append(t)except:breakdef handle_client(self, client_sock):addr = self.client_addresses.get(client_sock, ("未知", 0))while self.is_running:try:data = client_sock.recv(1024)if not data:breakmsg = data.decode("utf-8")self.log(f"{addr[0]}:{addr[1]} 说: {msg}")# 广播给所有人(包括发送者自己,让其也能通过网络收到回显)self.broadcast(f"[{addr[0]}:{addr[1]}]: {msg}")except:breakself.log(f"客户端 {addr[0]}:{addr[1]} 断开连接")if client_sock in self.client_sockets:self.client_sockets.remove(client_sock)if client_sock in self.client_addresses:del self.client_addresses[client_sock]self.update_client_list()try:client_sock.close()except:passdef broadcast(self, msg):"""向所有在线客户端发送消息(包括发送者自己)"""for sock in self.client_sockets[:]:  # 使用副本避免迭代时修改try:sock.send(msg.encode("utf-8"))except:passdef send_server_message(self):"""服务端主动发送消息"""if not self.is_running:returntext = self.server_msg_entry.get().strip()if not text:returnfull_msg = f"[服务端]: {text}"self.log(f"服务端说: {text}")self.broadcast(full_msg)self.server_msg_entry.delete(0, tk.END)def stop_server(self):self.is_running = False# 关闭所有客户端连接for sock in self.client_sockets:try:sock.close()except:passself.client_sockets.clear()self.client_addresses.clear()if self.server_socket:try:self.server_socket.close()except:passself.update_client_list()self.set_server_input_state(tk.DISABLED)self.log("服务端已停止")self.start_btn.config(state=tk.NORMAL)self.stop_btn.config(state=tk.DISABLED)def on_closing(self):self.stop_server()self.root.destroy()

---------------------------- 客户端模块 ----------------------------

class ClientWindow:
def init(self, root):
self.root = root
self.root.title("聊天室 - 客户端")
self.root.geometry("450x450")
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

    self.client_socket = Noneself.connected = Falseself.message_queue = queue.Queue()# ------ 连接设置 ------frame_top = tk.LabelFrame(root, text="连接设置")frame_top.pack(padx=5, pady=5, fill=tk.X)tk.Label(frame_top, text="IP:").grid(row=0, column=0, sticky=tk.E)self.ip_var = tk.StringVar(value="127.0.0.1")tk.Entry(frame_top, textvariable=self.ip_var, width=12).grid(row=0, column=1, padx=5)tk.Label(frame_top, text="端口:").grid(row=0, column=2, sticky=tk.E)self.port_var = tk.StringVar(value="5000")tk.Entry(frame_top, textvariable=self.port_var, width=6).grid(row=0, column=3, padx=5)tk.Label(frame_top, text="昵称:").grid(row=0, column=4, sticky=tk.E)self.nick_var = tk.StringVar(value="用户")tk.Entry(frame_top, textvariable=self.nick_var, width=10).grid(row=0, column=5, padx=5)self.connect_btn = tk.Button(frame_top, text="连接", command=self.toggle_connection)self.connect_btn.grid(row=0, column=6, padx=10)# ------ 聊天显示 ------self.chat_text = scrolledtext.ScrolledText(root, state='normal', wrap=tk.WORD)self.chat_text.pack(padx=5, pady=5, fill=tk.BOTH, expand=True)# ------ 发送区域 ------frame_bottom = tk.Frame(root)frame_bottom.pack(padx=5, pady=5, fill=tk.X)self.msg_entry = tk.Entry(frame_bottom)self.msg_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0,5))self.msg_entry.bind("<Return>", lambda e: self.send_message())self.msg_entry.config(state=tk.DISABLED)self.send_btn = tk.Button(frame_bottom, text="发送", command=self.send_message, state=tk.DISABLED)self.send_btn.pack(side=tk.RIGHT)self.update_gui_from_queue()def update_gui_from_queue(self):try:while True:task, data = self.message_queue.get_nowait()if task == "show_chat":self.chat_text.insert(tk.END, data + "\n")self.chat_text.see(tk.END)elif task == "set_input_state":self.msg_entry.config(state=data)self.send_btn.config(state=data)elif task == "set_connect_btn":self.connect_btn.config(text=data[0], command=data[1])except queue.Empty:passfinally:self.root.after(100, self.update_gui_from_queue)def show_chat(self, msg):self.message_queue.put(("show_chat", msg))def toggle_connection(self):if not self.connected:self.connect_server()else:self.disconnect()def connect_server(self):ip = self.ip_var.get().strip()port_str = self.port_var.get().strip()if not port_str.isdigit():messagebox.showerror("错误", "端口必须是数字")returnport = int(port_str)try:self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.client_socket.connect((ip, port))except Exception as e:messagebox.showerror("连接失败", f"无法连接到服务器: {e}")returnself.connected = Trueself.show_chat("已连接到服务器")self.message_queue.put(("set_input_state", tk.NORMAL))self.message_queue.put(("set_connect_btn", ("断开", self.toggle_connection)))recv_thread = threading.Thread(target=self.receive_loop, daemon=True)recv_thread.start()def disconnect(self):self.connected = Falseif self.client_socket:try:self.client_socket.close()except:passself.show_chat("已断开连接")self.message_queue.put(("set_input_state", tk.DISABLED))self.message_queue.put(("set_connect_btn", ("连接", self.toggle_connection)))def receive_loop(self):while self.connected:try:data = self.client_socket.recv(1024)if not data:breakself.show_chat(data.decode("utf-8"))except:breakif self.connected:self.show_chat("与服务器的连接已断开")self.disconnect()def send_message(self):if not self.connected:returntext = self.msg_entry.get().strip()if not text:returnnick = self.nick_var.get().strip() or "用户"full_msg = f"{nick}: {text}"try:self.client_socket.send(full_msg.encode("utf-8"))# 不再本地回显,因为服务端会广播给所有人(包括自己),避免重复self.msg_entry.delete(0, tk.END)except Exception as e:self.show_chat(f"发送失败: {e}")def on_closing(self):self.disconnect()self.root.destroy()

---------------------------- 主入口 ----------------------------

def main():
root = tk.Tk()
root.title("聊天室 - 选择角色")
root.geometry("250x150")

def open_server():win = tk.Toplevel()ServerWindow(win)def open_client():win = tk.Toplevel()ClientWindow(win)tk.Label(root, text="请选择角色", font=("微软雅黑", 12)).pack(pady=10)
tk.Button(root, text="启动服务端", width=15, command=open_server).pack(pady=5)
tk.Button(root, text="启动客户端", width=15, command=open_client).pack(pady=5)root.mainloop()

if name == "main":
main()
(1)分析关键代码的功能和使用方法(含有AI生成内容)
关键代码功能
服务端模块:
a.启动服务start_server():创建TCP套接字,绑定 0.0.0.0 和指定端口,开始监听。使用 SO_REUSEADDR 避免重启时端口占用。启动后使用新线程循环接受客户端连接 accept_clients()。
b.接收客户端连接 accept_clients():在独立线程中不断 accept(),将新客户端 socket 存入列表,并为每个客户端启动 handle_client() 线程,实现多客户端并发。
c.处理客户端消息 handle_client(client_sock):循环接收客户端数据,解码后记录日志,并调用 broadcast() 转发给所有已连接客户端。当接收空数据或异常时断开连接,清理资源。
d.广播消息 broadcast(msg):遍历客户端 socket 列表(使用副本),向每个连接发送编码后的消息。服务端主动发送的消息也通过此方法发给所有客户端,实现了服务端参与聊天的功能。
e.服务端输入发送 send_server_message():从界面输入框获取文字,加上 [服务端] 前缀后调用 broadcast()。输入框状态由 set_server_input_state() 控制,服务停止时禁用,启动后启用。
f.线程安全 UI 更新:通过 queue.Queue 和 after 机制,将日志输出、客户端列表更新、输入框状态修改等界面操作统一放入队列,由主线程定时取出执行,避免 tkinter 的线程安全问题。

客户端模块:
a.连接服务器 connect_server():根据用户输入的 IP 和端口建立 TCP 连接,成功后启动接收线程 receive_loop(),并将输入框、发送按钮设为可用。
b.接收消息 receive_loop():线程中循环接收服务器广播的数据,解码后通过队列安全地显示在聊天区域。若连接断开,自动更新界面状态。
c.发送消息 send_message():将输入内容加上昵称前缀发送给服务端。为保持界面洁净,发送后清空输入框,不再本地回显(依赖服务端广播回显)。
d.界面状态控制:连接/断开时通过队列更新按钮文本和输入框状态,防止用户在未连接时发送消息。
使用方法
a.运行程序,弹出角色选择窗口。
b.启动服务端:点击“启动服务端”,在新窗口设置端口(默认 5000),点击“启动服务”。此时服务端下方的输入框可用。
c.启动客户端:点击“启动客户端”,填写服务端 IP、端口、昵称,点击“连接”。
d.服务端和客户端均可输入文字,按回车或点“发送”按钮,所有参与者都能看到带前缀的消息。
e.服务端停止会断开所有客户端;客户端点“断开”或关闭窗口也可退出。
(2)分析生成程序的优点(含有部分AI生成内容)
a.界面直观,操作简便
使用 tkinter 提供了友好的图形界面,角色选择窗口清晰,服务端与客户端窗口布局一致,降低了使用门槛;
状态按钮自动切换,如“连接”↔“断开”、“启动服务”↔“停止服务”;输入框随连接状态自动启/禁用,交互逻辑合理。
b.实时多客户端通信能力强
服务端采用多线程架构,每个客户端连接和消息处理都在独立线程中进行,不会相互阻塞,可同时服务多个客户端;
消息通过服务端中转广播,确保所有在线用户都能实时收到信息,实现了简单的聊天室模型。
c.线程安全设计保障稳定性
所有跨线程的 UI 更新都通过 queue.Queue + after() 转发到主线程,避免了 tkinter 常见的崩溃问题;
广播时使用列表副本 self.client_sockets[:] 遍历,减少了迭代过程中列表被修改的风险。
d.资源管理与错误处理较完善
程序关闭时主动停止服务、断开客户端、关闭 socket,防止资源泄露;
捕获连接异常并用消息框提示,不会因单点故障导致整个程序崩溃;
使用 SO_REUSEADDR 允许端口快速复用,便于调试和重启。
e.代码结构清晰,易于扩展
服务端和客户端分别封装为独立的类,职责分明;
日志、用户列表、消息广播等功能模块化,后续可方便地添加数据库记录、加密、用户认证等功能;
AI生成的内容在方法命名和注释上有较好的可读性,便于新手理解和修改。
f.跨平台兼容
基于 Python 标准库(tkinter、socket、threading),只要安装 Python 3 即可在 Windows、macOS、Linux 上运行,不依赖第三方包。
(3)给出运行过程和结果截图
a.server端视角
LLM server
b.client端视角
LLM client

3. 实验过程中遇到的问题和解决过程

  • 问题1:队友电脑作为server无法接收到信息
  • 问题1解决方案:在上课时寻求老师的帮助,具体操作未知,不过老师在近15分钟的严肃探索中解决了问题!
  • 问题2:再次连接时发现仍存在无法接收信息的现象
  • 问题2解决方案:依据老师的提示与指导,关停了本人及队友电脑中的防火墙、联想电脑管家的运行。
  • 问题3:LLM大模型第一次生成的程序服务器没有回复功能
  • 问题3解决方案:向LLM再次输入提示词,增加回复功能。

其他(感悟、思考等)

本次实验在难度上较前两次有明显提升,最初我与队友均感到无从下手。配置 IP 地址时,原以为仅需填入地址即可建立连接,但实际操作中反复出现“连接超时”的提示。我们尝试关闭防火墙、切换至同一无线网络、使用 ipconfig 命令核对地址等方法,都没有解决问题,最终在老师的帮助下,客户端与服务端才终于成功互通、接收到对方消息。
通过这次实验,我深刻体会到,课堂上许多概念看似理解,动手实现时才会暴露真正的知识盲区,例如无法接收通信的问题,都是在反复调试中才真正掌握的。同时我也认识到,大语言模型虽是提升效率的工具,但必须结合自身对需求的分析与判断,进行多次修正,不能盲目照搬。与队友分工协作、各自负责服务端与客户端的排查调试,也让我意识到有条理的合作远胜于个人的反复尝试。可以说,这次实验不仅强化了应用编程的能力,更让我在持续试错与调整中,培育了面对复杂问题时从容应对的耐心。

http://www.jsqmd.com/news/779417/

相关文章:

  • 解决SEGGER_RTT_printf无法打印浮点数问题
  • 使用技巧(四):还在手写Hooks脚本?五个现成插件装好就生效,拦截删文件、护密钥、强制测试
  • aghub:GitHub开发者效率工具集,批量克隆、仓库管理与自动化实战
  • 2026年晶晨股份数字IC笔试试卷带答案
  • 搜维尔科技:利用MANUS数据手套扩展人形机器人操作数据采集规模
  • 2026年Java面试最全避坑指南:从基础、并发、JVM到微服务,这一篇就够了
  • 公司内网 git clone提示fatel失败
  • 写论文怎么给英文降AI?从97%降至8%的4种高效方法(附实测指南) - 殷念写论文
  • 基于51单片机智能声光双控红外人体感应路灯台灯路灯设计18-785
  • 从 C++ 到 Rust:不是更好的模样,而是另一套答案
  • 20260508 0
  • ESP32无人机远程识别模块:完整开源架构与安全集成实现指南
  • Snap.Hutao:免费高效的原神工具箱完全使用指南
  • 黑客赚钱的路子有多野?CTF逆向入门指南
  • Rocky linux 10.1 ARM版本系统安装
  • 如何快速入门 Kubernetes 网络配置?
  • 户外徒步戴运动耳机哪款好?盘点十款实用性价比运动耳机测评分享
  • 从单Agent协作到多Agent并行:收藏这份AI编程协作新范式指南,小白也能轻松掌握大模型
  • 从Kryo核心到Symphony系统:探秘移动SoC异构计算与能效协同设计
  • 认知神经科学研究报告【20260035】
  • 2026年北京君正数字IC笔试试卷带答案
  • 从 Claude Code 看 Harness Engineer 的设计
  • 20242210实验三《Python程序设计》实验报告
  • 3分钟配置Spyder深色模式:Python开发者的护眼终极指南
  • 2026教程:将整个项目Wiki交给Gemini 3.1 Pro,问答精度实测
  • LLM应用开发中的令牌管理:token-discipline项目详解与实践指南
  • 使用 Stream 流处理集合时如何避免中间结果占用过高内存?
  • 从“PPT小白”到“大神”,这些网站你必须知道!
  • 用Google ADK从零搭一个能调工具的AI Agent:Python实操全过程
  • 周红伟SEO能力加强和客户转化的能力点