You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,主窗口退出时线程不会自动终止,可能残留无效资源,但这不是导致无法回复的核心原因。

解决思路与修正代码

核心修复点

  1. 删除send()中创建新recv线程的代码,只在connect()中启动一次接收线程,全程复用该线程监听消息。
  2. 将recv线程设为守护线程,确保主程序退出时线程自动终止。
  3. 优化消息显示格式,区分自己和对方的消息,提升交互体验。
  4. 添加基础的异常捕获,避免程序因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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 19:20:37