Python Socket并行收发异常排查:Netcat克隆无法回复消息问题
Netcat克隆工具无法回复消息的问题分析与解决
问题描述
我开发了一款Netcat克隆工具,目的是提升自身技术能力,同时用于CTF场景(自研TCP客户端会更便捷)。工具整体功能基本正常,但存在一个异常:当通信对方发送消息后,我无法回复消息(对方的消息可以正常显示)。我已经尝试过多种教程方案(比如代码中已经实现了双线程思路),但都没能解决问题,希望能帮忙指出错误并提供解决思路。
项目源代码
import tkinter as tk from scrolledtext import * #it`s mostly the gui part, all the socket stuff is in connect(), import socket, threading #send() and recv() def connect(): global s, reader s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) addrlbl.configure(state='disabled') addr = addrntr.get() addrntr.configure(state='disabled') portlbl.configure(state='disabled') port = portntr.get() portntr.configure(state='disabled') connbtn.configure(text='CONNECTED',state='disabled') msginput.configure(state='normal') s.connect((addr, int(port))) reader = threading.Thread(target=recv, daemon=False) reader.start() def send(event): global s, reader msg = msginput.get() msginput.delete(0, tk.END) writero(chat, msg) s.sendall(msg.encode()) reader = threading.Thread(target=recv, daemon=False) reader.start() def recv(): global s while True: data = s.recv(4096).decode() if not data: writero(chat, '\n') break writero(chat, data) #IDEA: def writero(chat, msg): #our msg is sent normally chat.configure(state='normal') #bot msg is sent like: --><msg> chat.insert('end', msg) chat.configure(state='disabled') def srcopen(): from tkinter.filedialog import askopenfilename filepath = askopenfilename(filetypes=[("Python scripts", "*.py"), ("All Files", "*.*")]) srclabel['text'] = filepath if not filepath: return window = tk.Tk() chat = ScrolledText() chat.insert('end', "Here is your conversation:") chat.config(state='disabled') msginput = tk.Entry(width=110) connframe = tk.Frame(master=window, relief=tk.GROOVE) addrlbl = tk.Label(text='Address:', master=connframe) addrntr = tk.Entry(master=connframe) portlbl = tk.Label(text='Port:', master=connframe) portntr = tk.Entry(master=connframe) connbtn = tk.Button(text='CONNECT', master=connframe, command=connect) srcbtn = tk.Button(text='Open script', command=srcopen) srclabel = tk.Label(text='Script not attached...') chat.grid(sticky = "nw", column=0, row=0) msginput.grid(sticky="nw", column=0, row=1) chat.configure(state='disabled') msginput.configure(state='disabled') connframe.grid(sticky="nw", column=1, row=0, padx=10) addrlbl.grid(column=0, row=0) addrntr.grid(column=0, row=1) portlbl.grid(column=0, row=2) portntr.grid(column=0, row=3) connbtn.grid(column=0, row=4) srclabel.grid(column=1, row=1) srcbtn.grid(column = 1, row = 2) msginput.bind('<Return>', send) window.mainloop()
错误分析
- 重复创建接收线程:
connect()函数中已经启动了一个监听消息的recv线程,但每次调用send()时,又重新创建并启动一个新的recv线程。多个线程同时调用s.recv()会抢占socket资源,导致后续的send操作被阻塞,或者socket状态异常,无法正常发送消息。 - 线程未设为守护线程:当前
recv线程的daemon=False,主窗口退出时线程不会自动终止,可能残留无效资源,但这不是导致无法回复的核心原因。
解决思路与修正代码
核心修复点
- 删除
send()中创建新recv线程的代码,只在connect()中启动一次接收线程,全程复用该线程监听消息。 - 将
recv线程设为守护线程,确保主程序退出时线程自动终止。 - 优化消息显示格式,区分自己和对方的消息,提升交互体验。
- 添加基础的异常捕获,避免程序因socket异常崩溃。
修正后的代码
import tkinter as tk from scrolledtext import * import socket, threading def connect(): global s, reader s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) addrlbl.configure(state='disabled') addr = addrntr.get() addrntr.configure(state='disabled') portlbl.configure(state='disabled') port = portntr.get() portntr.configure(state='disabled') connbtn.configure(text='CONNECTED',state='disabled') msginput.configure(state='normal') try: s.connect((addr, int(port))) # 设置为守护线程,主程序退出时自动终止 reader = threading.Thread(target=recv, daemon=True) reader.start() except Exception as e: writero(chat, f"\n连接失败: {str(e)}") # 恢复控件状态 addrlbl.configure(state='normal') addrntr.configure(state='normal') portlbl.configure(state='normal') portntr.configure(state='normal') connbtn.configure(text='CONNECT',state='normal') msginput.configure(state='disabled') def send(event): global s msg = msginput.get() if not msg: return msginput.delete(0, tk.END) # 标记自己发送的消息 writero(chat, f"\n[我]: {msg}") try: s.sendall(msg.encode()) except Exception as e: writero(chat, f"\n发送失败: {str(e)}") def recv(): global s while True: try: data = s.recv(4096).decode() if not data: writero(chat, "\n连接已断开") break # 标记对方发送的消息 writero(chat, f"\n[对方]: {data}") except Exception as e: writero(chat, f"\n接收异常: {str(e)}") break def writero(chat, msg): chat.configure(state='normal') chat.insert('end', msg) # 自动滚动到最新消息 chat.see('end') chat.configure(state='disabled') def srcopen(): from tkinter.filedialog import askopenfilename filepath = askopenfilename(filetypes=[("Python scripts", "*.py"), ("All Files", "*.*")]) srclabel['text'] = filepath if not filepath: return window = tk.Tk() window.title("Netcat 克隆工具") chat = ScrolledText() chat.insert('end', "欢迎使用Netcat克隆工具,请先连接目标地址\n") chat.config(state='disabled') msginput = tk.Entry(width=110) connframe = tk.Frame(master=window, relief=tk.GROOVE) addrlbl = tk.Label(text='目标地址:', master=connframe) addrntr = tk.Entry(master=connframe) portlbl = tk.Label(text='端口:', master=connframe) portntr = tk.Entry(master=connframe) connbtn = tk.Button(text='连接', master=connframe, command=connect) srcbtn = tk.Button(text='打开脚本', command=srcopen) srclabel = tk.Label(text='未选择脚本...') chat.grid(sticky = "nsew", column=0, row=0, padx=5, pady=5) msginput.grid(sticky="ew", column=0, row=1, padx=5, pady=5) connframe.grid(sticky="nw", column=1, row=0, padx=10, pady=5) addrlbl.grid(column=0, row=0, pady=2) addrntr.grid(column=0, row=1, pady=2) portlbl.grid(column=0, row=2, pady=2) portntr.grid(column=0, row=3, pady=2) connbtn.grid(column=0, row=4, pady=10) srclabel.grid(column=1, row=1, padx=5) srcbtn.grid(column = 1, row = 2, padx=5) # 让聊天框自适应窗口大小 window.grid_columnconfigure(0, weight=1) window.grid_rowconfigure(0, weight=1) msginput.bind('<Return>', send) window.mainloop()
内容的提问来源于stack exchange,提问作者floataqq
相关产品推荐
相关产品推荐

