import socket
import threading
import tkinter as tk
from tkinter import filedialog # 用于提供打开文件对话框功能
import logging
import time
from datetime import datetime

# 日志配置
logging.basicConfig(level=logging.CRITICAL, format=‘%(asctime)s:%(levelname)s:%(message)s’)
file_handler = logging.FileHandler(‘TCPClient.log’)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(‘%(asctime)s:%(levelname)s:%(message)s’))
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter(‘%(asctime)s:%(levelname)s:%(message)s’))
logger = logging.getLogger()
logger.addHandler(file_handler)
logger.addHandler(console_handler)

packet_file_path = ‘’
send_thread = None # 全局发送线程
stop_send_flag = False # 停止发送标志

# 读取16进制数据包
def read_hex_packets(file_path):
    packets = []
    try:
        with open(file_path, “r”) as f:
            lines = f.readlines()
            for line in lines:
                line = line.strip() # 去除首尾空格和换行符
                if not line:
                    continue # 如果该行是空行,则直接跳过
                try:
                    packet = bytes.fromhex(line) # 将16进制转换成bytes格式
                except ValueError:
                    logging.error(“无效的数据包格式: %s”, line)
                    continue
                packets.append(packet) # 添加有效的数据包到列表中
    except IOError as e:
        logging.error(“读取文件 %s 失败: %s”, file_path, str(e))
    return packets

# 发送数据包
def send_packets():
    global stop_send_flag

    # 获取服务器IP地址、端口号、等待响应和发送间隔等参数
    server_ip = server_ip_entry.get()
    server_port = int(server_port_entry.get())
    wait_for_response = wait_for_response_var.get()
    send_interval = int(send_interval_entry.get())
    loop_send = loop_send_var.get()

    packets = read_hex_packets(packet_file_path) # 读取文件中的数据包

    try:
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP套接字
    except socket.error as e:
        logging.error(“创建套接字失败: %s”, str(e))
        return

    try:
        client_socket.connect((server_ip, server_port)) # 连接服务器

        # 发送和接收信息显示框清空
        info_text.delete(‘1.0’, tk.END)
        response_text.delete(‘1.0’, tk.END)

        while not stop_send_flag:
            for i, packet in enumerate(packets):
                try:
                    client_socket.sendall(packet) # 发送数据包
                except socket.error as e:
                    logging.error(“发送第 %d 个数据包失败: %s”, i+1, str(e))
                    info_text.insert(tk.END, “[%s] 发送第 %d 个数据包失败: %s\n” % (datetime.now().strftime(“%Y-%m-%d %H:%M:%S”), i+1, str(e)))
                    info_text.see(tk.END)
                    continue
                logging.info(“发送第 %d 个数据包: %s”, i+1, packet.hex())
                info_text.insert(tk.END, “[%s] TX %d: %s\n” % (datetime.now().strftime(“%Y-%m-%d %H:%M:%S”), i+1, packet.hex()))
                info_text.see(tk.END)

                if wait_for_response:
                    try:
                        response_packet = client_socket.recv(1024)
                        logging.info(“接收第 %d 个数据包的响应: %s”, i+1, response_packet.hex())
                        response_text.insert(tk.END, “[%s] RX %d: %s\n” % (datetime.now().strftime(“%Y-%m-%d %H:%M:%S”), i+1, response_packet.hex()))
                        response_text.see(tk.END)
                    except socket.error as e:
                        logging.error(“接收第 %d 个数据包的响应失败: %s”, i+1, str(e))
                        response_text.insert(tk.END, “接收第 %d 个数据包的响应失败: %s\n” % (i+1, str(e)))
                        response_text.see(tk.END)

                if i != len(packets)-1: # 如果不是最后一个数据包,则等待指定的发送间隔后再发送下一个数据包
                    time.sleep((send_interval) / 1000)

            if not loop_send: # 如果不是循环发送,则跳出循环
                break

    except socket.error as e:
        logging.error(str(e))

    finally:
        client_socket.close() # 关闭套接字

# 开始发送按钮回调函数
def start_send():
    global stop_send_flag, send_thread
    stop_send_flag = False # 置停止发送标志为False
    send_thread = threading.Thread(target=send_packets) # 创建发送线程

    if log_var.get():
        logger.setLevel(logging.INFO)
    else:
        logger.setLevel(logging.CRITICAL)

    send_thread.start() # 启动线程
    # 以下两行代码用于将信息显示框和响应显示框滚动到最底部
    info_text.see(tk.END)
    response_text.see(tk.END)

# 停止发送按钮回调函数
def stop_send():
    global stop_send_flag
    stop_send_flag = True # 置停止发送标志为True

# 打开文件对话框回调函数
def open_file_dialog():
    global packet_file_path
    packet_file_path = filedialog.askopenfilename(filetypes=((“文本文件”, “*.txt”),)) # 打开文本文件对话框
    file_path_label.config(text=packet_file_path) # 更新选择文件路径的Label的文本

# 创建GUI界面
root = tk.Tk()
root.title(“TCP客户端-循环发送”)

# 服务器IP和端口号输入框
server_frame = tk.Frame()
server_frame.pack(side=tk.TOP, padx=10, pady=10)
server_ip_label = tk.Label(server_frame, text="服务器IP地址: ")
server_ip_label.pack(side=tk.LEFT)
server_ip_entry = tk.Entry(server_frame, width=15)
server_ip_entry.pack(side=tk.LEFT)
server_port_label = tk.Label(server_frame, text="服务器端口号: ")
server_port_label.pack(side=tk.LEFT)
server_port_entry = tk.Entry(server_frame, width=5)
server_port_entry.pack(side=tk.LEFT)

# 文件选择和发送间隔输入框
file_frame = tk.Frame()
file_frame.pack(side=tk.TOP, padx=10, pady=10)
open_file_button = tk.Button(file_frame, text=“选择数据包文本文件”, command=open_file_dialog)
open_file_button.pack(side=tk.LEFT)
file_path_label = tk.Label(file_frame, text=“未选择任何文件”)
file_path_label.pack(side=tk.LEFT, padx=10)
send_interval_label = tk.Label(file_frame, text="数据包发送间隔(毫秒): ")
send_interval_label.pack(side=tk.LEFT)
send_interval_entry = tk.Entry(file_frame, width=5)
send_interval_entry.insert(tk.END, “500”) # 设置默认值为500毫秒
send_interval_entry.pack(side=tk.LEFT)

# 等待响应和循环发送复选框
wait_for_response_var = tk.BooleanVar