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

学号 2025-2026-2 《Python程序设计》实验x报告

课程:《Python程序设计》
班级: 2541
姓名: 浦馨宇
学号:20254119
实验教师:王志强
实验日期:2026年4月28日
必修/选修: 专选课

1.实验内容

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

要求1:

(1)创建服务端和客户端,选择一个通信端口,用Python语言编程实现通信演示程序;
(2)要求发送方输入内容,并传输;接收方收到信息并显示。

要求2:使用LLM生成一个带图形界面的程序

(1)分析关键代码的功能和使用方法
(2)分析生成程序的优点
(3)给出运行过程和结果截图
(4)程序代码托管到码云。

2. 实验过程及结果

要求1:

(1)根据老师创建服务端与客户端代码,进行一些基础理解,分析代码的作用,注释如图:
微信图片_20260503214108_574_191

(2)在cmd里用ipconfig查看IP地址,并在操作面板里修改自己的IP地址;
(3)与队友(20254114刘小萌)一起修改代码里的服务器IP地址与端口号,并运行程序:
我作为服务端,HOST填我的IP,运行并等待对方连接,连接上即可进行跨设备通讯,成果如下图:
微信图片_20260430201613_1082_16

我作为客户端,HOST填队友的IP,运行并连接上对方,通讯结果如下图:
微信图片_20260430201216_1081_16

(4)代码托管地址:https://gitee.com/ping2127/python-games-ping2127/blob/master/SockeServer.py

要求2:

(1)让deepseek生成一个带图形界面的程序,以服务端为例,代码如下:

点击查看代码
# gui_server_stable.py - 稳定版服务端
# 实验三:Socket编程技术

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

class StableServer:
    def __init__(self):
        """初始化服务端"""
        
        # ==========================================
        # 👇 修改这里的信息
        # ==========================================
        self.student_name = "浦馨宇"
        self.student_id = "20254119"
        self.partner_name = "刘小萌"
        self.partner_id = "20254114"
        # ==========================================
        
        # 创建窗口
        self.window = tk.Tk()
        self.window.title(f"服务端-{self.student_name}")
        self.window.geometry("650x700")
        
        # 网络设置
        self.server_socket = None
        self.client_socket = None
        self.client_address = None
        self.is_running = False
        self.is_connected = False
        
        # 重连设置
        self.reconnect_attempts = 0  # 当前重连次数
        self.max_reconnect = 10      # 最大重连次数
        
        # 创建界面
        self.setup_ui()
        self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.window.after(500, self.show_my_ip)
    
    def setup_ui(self):
        """设置界面"""
        
        # 标题
        title_frame = tk.Frame(self.window)
        title_frame.pack(pady=10)
        tk.Label(title_frame, text="Socket实验-服务端(稳定版)", 
                font=("Arial", 14, "bold")).pack()
        tk.Label(title_frame, 
                text=f"姓名: {self.student_name}  学号: {self.student_id}",
                font=("Arial", 10)).pack()
        
        # IP显示
        ip_frame = tk.Frame(self.window, relief=tk.RIDGE, borderwidth=2)
        ip_frame.pack(pady=10, padx=20, fill=tk.X)
        tk.Label(ip_frame, text="📡 本机IP(告诉队友)", 
                font=("Arial", 10, "bold"), fg="red").pack(pady=5)
        self.ip_label = tk.Label(ip_frame, text="获取中...", 
                                font=("Arial", 12), fg="blue")
        self.ip_label.pack(pady=5)
        
        # 控制区
        control_frame = tk.Frame(self.window)
        control_frame.pack(pady=10)
        
        self.start_btn = tk.Button(control_frame, text="▶ 启动服务器",
                                  command=self.toggle_server,
                                  width=15, height=2, bg="lightgreen")
        self.start_btn.pack(side=tk.LEFT, padx=10)
        
        tk.Label(control_frame, text="端口:").pack(side=tk.LEFT)
        self.port_entry = tk.Entry(control_frame, width=8)
        self.port_entry.insert(0, "8888")
        self.port_entry.pack(side=tk.LEFT, padx=5)
        
        # 连接信息
        info_frame = tk.Frame(self.window)
        info_frame.pack(pady=5)
        self.conn_info_label = tk.Label(info_frame, text="", fg="gray")
        self.conn_info_label.pack()
        
        # 消息显示区
        tk.Label(self.window, text="通信记录:", 
                font=("Arial", 10, "bold")).pack(anchor=tk.W, padx=20)
        self.msg_area = scrolledtext.ScrolledText(
            self.window, height=18, width=70, state='disabled')
        self.msg_area.pack(padx=20, pady=5)
        
        # 发送区
        send_frame = tk.Frame(self.window)
        send_frame.pack(padx=20, pady=5, fill=tk.X)
        
        self.msg_entry = tk.Entry(send_frame, width=55, font=("Arial", 11))
        self.msg_entry.pack(side=tk.LEFT, padx=5)
        self.msg_entry.bind('<Return>', self.send_message)
        
        self.send_btn = tk.Button(send_frame, text="发送",
                                 command=self.send_message,
                                 width=8, bg="lightblue", state='disabled')
        self.send_btn.pack(side=tk.LEFT, padx=5)
        
        # 状态栏
        self.status_label = tk.Label(self.window, text="🔴 服务器未启动",
                                    font=("Arial", 10), fg="red")
        self.status_label.pack(pady=10)
    
    def show_my_ip(self):
        """显示本机IP"""
        try:
            hostname = socket.gethostname()
            local_ip = socket.gethostbyname(hostname)
            
            # 获取真实IP
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(("8.8.8.8", 80))
                real_ip = s.getsockname()[0]
                s.close()
            except:
                real_ip = local_ip
            
            self.ip_label.config(
                text=f"IP: {real_ip}  端口: {self.port_entry.get()}")
            
        except Exception as e:
            self.ip_label.config(text=f"获取失败: {e}")
    
    def log_message(self, message, msg_type="系统"):
        """显示消息"""
        self.msg_area.config(state='normal')
        time_str = datetime.now().strftime("%H:%M:%S")
        
        colors = {
            "接收": ("📩 队友:", "blue"),
            "发送": ("📤 我:", "green"),
            "成功": ("✅", "green"),
            "错误": ("❌", "red"),
            "警告": ("⚠️", "orange")
        }
        
        if msg_type in colors:
            prefix, color = colors[msg_type]
            self.msg_area.insert(tk.END, f"[{time_str}] {prefix} {message}\n", msg_type)
            self.msg_area.tag_config(msg_type, foreground=color)
        else:
            self.msg_area.insert(tk.END, f"[{time_str}] ℹ️ {message}\n")
        
        self.msg_area.see(tk.END)
        self.msg_area.config(state='disabled')
    
    def toggle_server(self):
        """切换服务器状态"""
        if not self.is_running:
            self.start_server()
        else:
            self.stop_server()
    
    def start_server(self):
        """启动服务器"""
        try:
            port = int(self.port_entry.get())
            
            # 创建socket,设置更多选项
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 设置keepalive(保持连接)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
            
            self.server_socket.bind(('0.0.0.0', port))
            self.server_socket.listen(5)
            
            self.is_running = True
            self.start_btn.config(text="⏹ 停止服务器", bg="lightcoral")
            self.port_entry.config(state='disabled')
            self.status_label.config(text="🟢 服务器运行中-等待连接...", fg="green")
            
            self.show_my_ip()
            self.log_message("=" * 50, "系统")
            self.log_message(f"服务器启动成功!端口: {port}", "成功")
            self.log_message(f"服务端: {self.student_name}({self.student_id})", "系统")
            self.log_message(f"等待队友 {self.partner_name} 连接...", "系统")
            self.log_message("=" * 50, "系统")
            
            # 启动接受连接的线程
            accept_thread = threading.Thread(target=self.accept_clients, daemon=True)
            accept_thread.start()
            
        except Exception as e:
            messagebox.showerror("启动失败", f"错误: {e}")
    
    def stop_server(self):
        """停止服务器"""
        self.is_running = False
        self.is_connected = False
        
        # 关闭客户端连接
        if self.client_socket:
            try:
                self.client_socket.shutdown(socket.SHUT_RDWR)
                self.client_socket.close()
            except:
                pass
            self.client_socket = None
        
        # 关闭服务器
        if self.server_socket:
            try:
                self.server_socket.close()
            except:
                pass
            self.server_socket = None
        
        self.start_btn.config(text="▶ 启动服务器", bg="lightgreen")
        self.send_btn.config(state='disabled')
        self.port_entry.config(state='normal')
        self.conn_info_label.config(text="")
        self.status_label.config(text="🔴 服务器已停止", fg="red")
        self.log_message("服务器已停止", "系统")
    
    def accept_clients(self):
        """接受客户端连接(循环接受,支持重连)"""
        while self.is_running:
            try:
                self.server_socket.settimeout(1)
                
                # 如果已有连接,跳过
                if self.is_connected and self.client_socket:
                    time.sleep(0.5)
                    continue
                
                # 接受新连接
                client_socket, address = self.server_socket.accept()
                
                # 设置客户端socket选项
                client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                client_socket.settimeout(30)  # 30秒超时
                
                self.client_socket = client_socket
                self.client_address = address
                self.is_connected = True
                self.reconnect_attempts = 0
                
                self.log_message(f"队友已连接!地址: {address}", "成功")
                self.conn_info_label.config(
                    text=f"已连接: {address[0]}:{address[1]}", fg="green")
                self.status_label.config(text="🟢 队友在线-可以聊天!", fg="green")
                self.send_btn.config(state='normal')
                self.msg_entry.focus()
                
                # 启动接收线程
                receive_thread = threading.Thread(
                    target=self.receive_messages, 
                    args=(client_socket, address),
                    daemon=True
                )
                receive_thread.start()
                
                # 启动心跳线程
                heartbeat_thread = threading.Thread(
                    target=self.send_heartbeat,
                    args=(client_socket,),
                    daemon=True
                )
                heartbeat_thread.start()
                
            except socket.timeout:
                continue
            except Exception as e:
                if self.is_running:
                    self.log_message(f"接受连接出错: {e}", "错误")
                time.sleep(1)
    
    def send_heartbeat(self, client_socket):
        """发送心跳包保持连接"""
        while self.is_connected and self.is_running:
            try:
                # 每10秒发送一个心跳包
                time.sleep(10)
                if self.is_connected:
                    client_socket.send(b'HEARTBEAT')
            except:
                break
    
    def receive_messages(self, client_socket, address):
        """接收客户端消息"""
        buffer = ""  # 消息缓冲区
        
        while self.is_connected and self.is_running:
            try:
                data = client_socket.recv(1024)
                
                if not data:
                    # 连接断开
                    break
                
                message = data.decode('utf-8')
                
                # 过滤心跳包
                if message == "HEARTBEAT":
                    continue
                
                # 显示消息
                self.log_message(message, "接收")
                
            except socket.timeout:
                # 超时,继续等待
                continue
            except ConnectionResetError:
                self.log_message("连接被重置", "警告")
                break
            except Exception as e:
                self.log_message(f"接收错误: {e}", "错误")
                break
        
        # 处理断开
        self.handle_disconnect(address)
    
    def handle_disconnect(self, address):
        """处理客户端断开"""
        self.is_connected = False
        
        if self.client_socket:
            try:
                self.client_socket.close()
            except:
                pass
            self.client_socket = None
        
        self.send_btn.config(state='disabled')
        self.conn_info_label.config(text="")
        
        if self.is_running:
            self.log_message(f"队友 {address} 断开连接", "警告")
            self.log_message("等待队友重新连接...", "系统")
            self.status_label.config(text="🟡 队友断开-等待重连...", fg="orange")
    
    def send_message(self, event=None):
        """发送消息"""
        if not self.is_connected or not self.client_socket:
            messagebox.showwarning("未连接", "请等待队友连接后再发送消息")
            return
        
        message = self.msg_entry.get().strip()
        if not message:
            return
        
        try:
            # 发送消息
            self.client_socket.send(message.encode('utf-8'))
            
            # 显示发送的消息
            self.log_message(message, "发送")
            
            # 清空输入框
            self.msg_entry.delete(0, tk.END)
            
        except ConnectionResetError:
            self.log_message("发送失败-连接断开", "错误")
            self.handle_disconnect(self.client_address if self.client_address else "未知")
        except Exception as e:
            self.log_message(f"发送失败: {e}", "错误")
    
    def on_closing(self):
        """关闭窗口"""
        if messagebox.askokcancel("退出", "确定要退出吗?"):
            self.stop_server()
            self.window.destroy()
    
    def run(self):
        """运行"""
        self.window.mainloop()

if __name__ == "__main__":
    server = StableServer()
    server.run()

(2)分析关键代码的功能及用法:
①导入的库:

import socket # 网络通信核心库,创建TCP/UDP连接
import threading # 多线程库,让程序同时做多件事
import tkinter as tk # GUI图形界面库,创建窗口按钮等
from tkinter import scrolledtext, messagebox # 滚动文本框+弹窗
from datetime import datetime # 获取当前时间,用于消息时间提示
import time # 时间相关,用于延时等待

②心跳保活,防止通讯一段时间不用就自己断开

while self.is_connected:
    time.sleep(10) # 等10秒
    client_socket.send(b'HEARTBEAT') # 发心跳包
    message = data.decode('utf-8')
        # 过滤心跳包,在接受信息之后不显示心跳包的内容,保持通讯界面简洁
        if message == "HEARTBEAT":
            continue

③搭建界面

# 标题区域
title_frame = tk.Frame(self.window) # 创建一个框架(容器)
title_frame.pack(pady=10) # 放到窗口里,上下留10像素间距
# 创建标签(显示文字)
tk.Label(title_frame, text="Socket实验-服务端(稳定版)", 
         font=("Arial", 14, "bold")).pack()

④创建TCP服务器,大部分已在前面分析过,所以着重分析LLM新增的

self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# SO_REUSEADDR: 允许重用地址,避免"端口被占用"错误
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# SO_KEEPALIVE: 保持连接活跃,防止长时间不通信断连

(3)运行过程和结果:
我做服务端:
微信图片_20260430203323_1086_16
我做客户端
微信图片_20260430203630_1087_16

可以看到V1.0程序最大的缺陷是没给服务端添加发送信息的功能,改进的V3.0结果如下:
微信图片_20260430211538_567_191

(4)分析LLM代码的优点:
①从运行窗口变为可视化窗口,窗口界面上会实时显示连接状态,操作易懂简便;
②可以自动获取本机的IP地址,不需要再自己查一遍;
③bind绑定的元组变成('0.0.0.0', port),可以监听所有网络接口;同时可以在窗口上直接输入IP地址和改端口号,不用每换一个人就改一遍代码;
④具备心跳保护、超时等待、自动重连和应对消息发送失败等丰富的错误应对机制,稳定性很强;

(5)代码托管地址:
服务端:https://gitee.com/ping2127/python-games-ping2127/blob/master/deepseek_python_socket(服务端3.0).py
客户端:https://gitee.com/ping2127/python-games-ping2127/blob/master/deepseek_python_socket(客户端3.0).py

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

  • 问题1:最开始链接的时候对方的客户端无法连接到服务器。
  • 问题1解决方案:检查两人的端口号和IP地址是否一致,发现不一致后改正,并先启动服务器再启动客户端,后来连接成功。
  • 问题2:在LLM代码运行过程中发现了第一次生成的代码出现无法做到通讯的问题。
  • 问题2解决方案:找出代码中的问题所在是没有己方消息输入的设计,并让大模型重新添加输入消息的代码板块。

其他(感悟、思考等)

这次实验带给我很大的一个感悟就是它开启了我学习Python的一扇新的大门。以前我有过学Python的基础,但都只停留在解决数学问题或是设计小游戏等,并不像这一次实验内容那么实用和深入。它让我见识到Python里边儿还有像socket的库这样功能十分丰富的库,也让我理解了Python能够调用库是多么方便。同时还让我发现,我对Python的学习其实只有皮毛,编程可以带来的自由和实惠内容非常广阔,这坚定了我把Python程序设计学好学扎实的信念。

参考资料

ZetCode 中文网

Python官方文档——socket库相关内容

posted @ 2026-05-03 23:25  浦馨宇  阅读(3)  评论(0)    收藏  举报