#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CH395F BSD Socket API 测试程序
用于测试 STM32F4 + CH395F 的网络功能

使用方法:
    python ch395f_socket_test.py [模式] [选项]

模式:
    tcp_server   - 作为 TCP 服务器（测试 CH395F TCP Client）
    tcp_client   - 作为 TCP 客户端（测试 CH395F TCP Server）
    udp_server   - 作为 UDP 服务器（测试 CH395F UDP Client）
    udp_client   - 作为 UDP 客户端（测试 CH395F UDP Server）
    tcp_echo     - TCP 回显服务器（测试双向通信）
    udp_echo     - UDP 回显服务器（测试双向通信）
    stress       - 压力测试（多连接并发测试）

选项:
    --ip         目标 IP 地址（默认: 192.168.1.100）
    --port       端口号（默认: 8080）
    --count      测试次数（默认: 10）
    --size       数据包大小（默认: 1024）

示例:
    # 测试 CH395F TCP Server
    python ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080

    # 测试 CH395F TCP Client
    python ch395f_socket_test.py tcp_server --port 8080

    # 测试 CH395F UDP Client
    python ch395f_socket_test.py udp_server --port 8888

    # 测试 CH395F UDP Server
    python ch395f_socket_test.py udp_client --ip 192.168.1.100 --port 8888

    # 压力测试（多连接）
    python ch395f_socket_test.py stress --ip 192.168.1.100 --port 8080 --count 7
"""

import socket
import sys
import time
import argparse
import threading
import struct
import random
import string
from datetime import datetime


class Colors:
    """终端颜色"""
    HEADER = '\033[95m'
    BLUE = '\033[94m'
    CYAN = '\033[96m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    END = '\033[0m'
    BOLD = '\033[1m'


def log_msg(msg, color=Colors.END):
    """带时间戳的日志输出"""
    timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
    print(f"{Colors.CYAN}[{timestamp}]{Colors.END} {color}{msg}{Colors.END}")


def log_success(msg):
    log_msg(f"✓ {msg}", Colors.GREEN)


def log_error(msg):
    log_msg(f"✗ {msg}", Colors.RED)


def log_info(msg):
    log_msg(f"ℹ {msg}", Colors.BLUE)


def log_warn(msg):
    log_msg(f"⚠ {msg}", Colors.YELLOW)


def generate_data(size):
    """生成测试数据"""
    return ''.join(random.choices(string.ascii_letters + string.digits, k=size)).encode()


def verify_data(data, expected_len):
    """验证数据"""
    if len(data) != expected_len:
        return False, f"长度不匹配: 期望 {expected_len}, 实际 {len(data)}"
    return True, "OK"


# ==================== TCP 测试函数 ====================

def tcp_server_test(args):
    """TCP 服务器测试（测试 CH395F TCP Client）"""
    log_info("=" * 60)
    log_info("TCP 服务器测试 - 测试 CH395F TCP Client 功能")
    log_info("=" * 60)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.settimeout(args.timeout)

    try:
        server_socket.bind(('0.0.0.0', args.port))
        server_socket.listen(1)
        log_success(f"服务器启动，监听端口 {args.port}")

        for conn_idx in range(3):
            try:
                server_socket.settimeout(args.timeout)
                log_info(f"等待第 {conn_idx+1} 个连接（超时 {args.timeout}s）...")
                client_socket, client_addr = server_socket.accept()
                log_success(f"连接#{conn_idx+1} 来自: {client_addr[0]}:{client_addr[1]}")
                client_socket.settimeout(5)

                # Test 1: 接收数据
                log_info("-" * 40)
                log_info(f"[连接{conn_idx+1}] 测试 1: 接收数据")
                try:
                    data = client_socket.recv(args.size)
                    if data:
                        log_success(f"收到数据 ({len(data)} bytes): {data[:50]}...")
                except socket.timeout:
                    log_error("接收超时")

                # Test 2: PING echo
                log_info(f"[连接{conn_idx+1}] 测试 2: 双向通信 (PING)")
                ping_ok = 0
                for i in range(5):
                    try:
                        test_data = f"PING_{i}".encode()
                        client_socket.send(test_data)
                        data = client_socket.recv(args.size)
                        if data and data == test_data:
                            log_success(f"回显匹配: {test_data.decode()}")
                            ping_ok += 1
                        elif data:
                            log_error(f"回显不匹配! 收到: {data[:20]}")
                            break
                    except socket.timeout:
                        log_error("通信超时")
                        break
                    except Exception as e:
                        log_error(f"通信错误: {e}")
                        break
                log_info(f"PING 匹配: {ping_ok}/5")

                # Test 3: 大包发送
                log_info(f"[连接{conn_idx+1}] 测试 3: 大包发送")
                for i in range(args.count):
                    try:
                        test_data = generate_data(min(100, args.size))
                        client_socket.send(test_data)
                        time.sleep(0.1)
                    except Exception as e:
                        log_error(f"发送错误: {e}")
                        break
                log_success(f"发送 {args.count} 包完成")

                client_socket.close()
                log_success(f"连接#{conn_idx+1} 测试完成")
                time.sleep(0.5)

            except socket.timeout:
                log_error(f"等待连接#{conn_idx+1} 超时")
                break
            except Exception as e:
                log_error(f"连接#{conn_idx+1} 错误: {e}")
                break

        log_success("全部连接测试完成")

    except socket.timeout:
        log_error("等待连接超时")
    except Exception as e:
        log_error(f"服务器错误: {e}")
    finally:
        server_socket.close()


def tcp_client_test(args):
    """TCP 客户端测试（测试 CH395F TCP Server）"""
    log_info("=" * 60)
    log_info("TCP 客户端测试 - 测试 CH395F TCP Server 功能")
    log_info("=" * 60)

    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.settimeout(10)

    try:
        # 连接服务器
        log_info(f"正在连接 CH395F TCP Server: {args.ip}:{args.port}")
        client_socket.connect((args.ip, args.port))
        log_success("连接成功!")

        # 测试发送数据
        log_info("-" * 40)
        log_info("测试 1: 发送数据")
        for i in range(args.count):
            try:
                test_data = generate_data(min(100, args.size))
                client_socket.send(test_data)
                log_success(f"发送数据 ({len(test_data)} bytes)")
                log_info(f"发送 hex: {' '.join(f'{b:02X}' for b in test_data[:16])}")

                # 等待回显
                data = client_socket.recv(args.size)
                if data:
                    log_info(f"接收 hex: {' '.join(f'{b:02X}' for b in data[:16])}")
                    log_info(f"接收长度: {len(data)}, 期望长度: {len(test_data)}")
                    if data == test_data:
                        log_success("回显数据匹配!")
                    else:
                        log_error("回显数据不匹配!")
                        # 找出第一个不同的字节
                        for j in range(min(len(data), len(test_data))):
                            if data[j] != test_data[j]:
                                log_error(f"首个差异: 位置 {j}, 发送={test_data[j]:02X}, 接收={data[j]:02X}")
                                break
                else:
                    log_error("收到空数据")
                    break

                time.sleep(0.1)
            except Exception as e:
                log_error(f"发送/接收错误: {e}")
                break

        # 测试大数据量
        log_info("-" * 40)
        log_info("测试 2: 大数据量传输")
        try:
            large_data = generate_data(min(1024, args.size))
            client_socket.send(large_data)
            log_success(f"发送大数据 ({len(large_data)} bytes)")

            received = b""
            while len(received) < len(large_data):
                chunk = client_socket.recv(min(1024, len(large_data) - len(received)))
                if not chunk:
                    break
                received += chunk

            if received == large_data:
                log_success("大数据传输完整!")
            else:
                log_error(f"大数据传输不完整: 发送 {len(large_data)}, 接收 {len(received)}")
        except Exception as e:
            log_error(f"大数据传输错误: {e}")

        # 测试快速收发
        log_info("-" * 40)
        log_info("测试 3: 快速收发")
        success_count = 0
        fail_count = 0
        start_time = time.time()

        for i in range(args.count):
            try:
                test_data = f"FAST_{i:04d}".encode()
                client_socket.send(test_data)
                data = client_socket.recv(100)
                if data and data == test_data:
                    success_count += 1
                else:
                    fail_count += 1
            except Exception as e:
                fail_count += 1

        elapsed = time.time() - start_time
        log_info(f"快速收发结果: 成功 {success_count}, 失败 {fail_count}, 耗时 {elapsed:.2f}s")

        client_socket.close()
        log_success("测试完成，连接已关闭")

    except ConnectionRefusedError:
        log_error("连接被拒绝 - CH395F TCP Server 未运行")
    except socket.timeout:
        log_error("连接超时")
    except Exception as e:
        log_error(f"客户端错误: {e}")
    finally:
        client_socket.close()


# ==================== UDP 测试函数 ====================

def udp_server_test(args):
    """UDP 服务器测试（测试 CH395F UDP Client）"""
    log_info("=" * 60)
    log_info("UDP 服务器测试 - 测试 CH395F UDP Client 功能")
    log_info("=" * 60)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.settimeout(30)

    try:
        server_socket.bind(('0.0.0.0', args.port))
        log_success(f"服务器启动，监听端口 {args.port}")
        log_info("等待 CH395F UDP Client 数据...")

        # 接收数据
        for i in range(args.count):
            try:
                data, client_addr = server_socket.recvfrom(args.size)
                log_success(f"收到来自 {client_addr[0]}:{client_addr[1]} ({len(data)} bytes)")
                log_info(f"数据: {data[:50]}...")

                # 回显数据
                server_socket.sendto(data, client_addr)
                log_info(f"回显数据到 {client_addr[0]}:{client_addr[1]}")
            except socket.timeout:
                log_warn("接收超时")
                break
            except Exception as e:
                log_error(f"接收错误: {e}")
                break

        log_success("测试完成")

    except Exception as e:
        log_error(f"服务器错误: {e}")
    finally:
        server_socket.close()


def udp_client_test(args):
    """UDP Echo 测试（测试 CH395F UDP Server）"""
    log_info("=" * 60)
    log_info("UDP Echo 测试")
    log_info("=" * 60)

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    dest = (args.ip, args.port)

    try:
        # Phase A: HELLO 探针等待 STM32 就绪
        log_info(f"Phase A: 等待 CH395F 就绪（超时 {args.timeout}s）")
        sock.settimeout(2)
        t0 = time.time()
        ready = False
        while time.time() - t0 < args.timeout:
            try:
                sock.sendto(b"HELLO", dest)
                data, addr = sock.recvfrom(64)
                if data == b"HELLO":
                    log_success(f"就绪 ({time.time()-t0:.1f}s, from {addr[0]}:{addr[1]})")
                    ready = True
                    break
            except socket.timeout:
                pass
        if not ready:
            log_error(f"未就绪（{args.timeout}s 超时）")
            return

        sock.settimeout(5)

        # Phase B: PING Echo
        log_info("-" * 40)
        log_info("Phase B: PING Echo（5 轮）")
        ok = 0
        for i in range(5):
            msg = f"PING_{i}".encode()
            try:
                sock.sendto(msg, dest)
                data, _ = sock.recvfrom(64)
                if data == msg:
                    ok += 1
                    log_success(f"PING_{i} OK")
                else:
                    log_error(f"PING_{i} mismatch: {data[:20]}")
                    break
            except socket.timeout:
                log_error(f"PING_{i} timeout")
                break
            time.sleep(0.02)
        log_info(f"PING: {ok}/5")

        # Phase C: 大包 Echo
        log_info("-" * 40)
        log_info(f"Phase C: 大包 Echo（{args.count} 轮）")
        for i in range(args.count):
            td = generate_data(min(100, args.size))
            try:
                sock.sendto(td, dest)
                data, _ = sock.recvfrom(args.size)
                if data == td:
                    log_success(f"#{i+1} OK ({len(data)}B)")
                else:
                    log_error(f"#{i+1} mismatch")
                    break
                time.sleep(0.02)
            except socket.timeout:
                log_warn(f"#{i+1} timeout")
            except Exception as e:
                log_error(f"#{i+1}: {e}")
                break

        log_success("Phase 3 完成")

    except Exception as e:
        log_error(f"错误: {e}")
    finally:
        sock.close()


# ==================== 回显测试 ====================

def tcp_echo_server_test(args):
    """TCP 回显服务器测试"""
    log_info("=" * 60)
    log_info("TCP 回显服务器测试 - 测试双向通信功能")
    log_info("=" * 60)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.settimeout(args.timeout)

    try:
        server_socket.bind(('0.0.0.0', args.port))
        server_socket.listen(1)
        log_success(f"回显服务器启动，端口 {args.port}")
        log_info(f"等待连接（超时 {args.timeout}s）...")

        client_socket, client_addr = server_socket.accept()
        log_success(f"连接来自: {client_addr[0]}:{client_addr[1]}")
        client_socket.settimeout(5)

        # 循环回显
        echo_count = 0
        start_time = time.time()

        log_info("开始回显测试 (按 Ctrl+C 停止)...")
        while echo_count < args.count:
            try:
                data = client_socket.recv(args.size)
                if not data:
                    log_warn("客户端断开连接")
                    break

                echo_count += 1
                client_socket.send(data)

                if echo_count % 10 == 0:
                    elapsed = time.time() - start_time
                    rate = echo_count / elapsed if elapsed > 0 else 0
                    log_info(f"回显 {echo_count} 次, 速率: {rate:.1f} msg/s")

            except socket.timeout:
                continue
            except Exception as e:
                log_error(f"回显错误: {e}")
                break

        elapsed = time.time() - start_time
        log_success(f"回显测试完成: {echo_count} 次, 耗时 {elapsed:.2f}s")

        client_socket.close()

    except Exception as e:
        log_error(f"服务器错误: {e}")
    finally:
        server_socket.close()


def udp_echo_server_test(args):
    """UDP 回显服务器测试"""
    log_info("=" * 60)
    log_info("UDP 回显服务器测试 - 测试双向通信功能")
    log_info("=" * 60)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.settimeout(60)

    try:
        server_socket.bind(('0.0.0.0', args.port))
        log_success(f"UDP 回显服务器启动，端口 {args.port}")
        log_info("等待数据...")

        echo_count = 0
        start_time = time.time()
        clients = set()

        while echo_count < args.count:
            try:
                data, client_addr = server_socket.recvfrom(args.size)
                if not data:
                    continue

                clients.add(client_addr)
                echo_count += 1

                # 回显数据
                server_socket.sendto(data, client_addr)

                if echo_count % 10 == 0:
                    elapsed = time.time() - start_time
                    rate = echo_count / elapsed if elapsed > 0 else 0
                    log_info(f"回显 {echo_count} 次, 客户端数: {len(clients)}, 速率: {rate:.1f} msg/s")

            except socket.timeout:
                continue
            except Exception as e:
                log_error(f"回显错误: {e}")
                break

        elapsed = time.time() - start_time
        log_success(f"回显测试完成: {echo_count} 次, 耗时 {elapsed:.2f}s")

    except Exception as e:
        log_error(f"服务器错误: {e}")
    finally:
        server_socket.close()


# ==================== 压力测试 ====================

def stress_test_thread(client_id, args, results, barrier):
    """压力测试线程"""
    thread_name = f"Client-{client_id}"
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(5)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    try:
        t0 = time.time()
        sock.connect((args.ip, args.port))
        local = sock.getsockname()
        remote = sock.getpeername()
        t_conn = (time.time() - t0) * 1000
        log_success(f"[{thread_name}] 连接成功 "
                    f"(local={local[0]}:{local[1]}, "
                    f"remote={remote[0]}:{remote[1]}, "
                    f"{t_conn:.0f}ms)")

        barrier.wait()

        success_count = 0
        fail_count = 0

        for i in range(args.count):
            try:
                test_data = f"T{client_id:02d}_{i:04d}".encode()
                t_send = time.time()
                sock.send(test_data)
                data = sock.recv(100)
                t_rtt = (time.time() - t_send) * 1000
                if data:
                    if data == test_data:
                        success_count += 1
                        log_info(f"[{thread_name}] #{i}: OK ({t_rtt:.0f}ms)")
                    else:
                        fail_count += 1
                        log_warn(f"[{thread_name}] #{i}: MISMATCH "
                                 f"want={len(test_data)}B/{test_data[:20].decode(errors='replace')!r} "
                                 f"got={len(data)}B/{data[:20].decode(errors='replace')!r}"
                                 f" ({t_rtt:.0f}ms)")
                else:
                    fail_count += 1
                    log_warn(f"[{thread_name}] #{i}: recv returned empty ({t_rtt:.0f}ms)")

            except socket.timeout:
                fail_count += 1
                t_elapsed = (time.time() - t_send) * 1000
                log_error(f"[{thread_name}] #{i}: TIMEOUT ({t_elapsed:.0f}ms)")
            except ConnectionResetError:
                fail_count += 1
                t_elapsed = (time.time() - t_send) * 1000
                log_error(f"[{thread_name}] #{i}: RESET by peer ({t_elapsed:.0f}ms)")
            except Exception as e:
                fail_count += 1
                t_elapsed = (time.time() - t_send) * 1000
                log_error(f"[{thread_name}] #{i}: {type(e).__name__}: {e} ({t_elapsed:.0f}ms)")

        results[client_id] = (success_count, fail_count)
        t_total = (time.time() - t0) * 1000
        log_info(f"[{thread_name}] 完成: 成功 {success_count}, 失败 {fail_count} ({t_total:.0f}ms)")

    except Exception as e:
        log_error(f"[{thread_name}] 连接失败: {type(e).__name__}: {e}")
        results[client_id] = (0, args.count)
    finally:
        sock.close()


def stress_test(args):
    """压力测试（多连接并发）"""
    log_info("=" * 60)
    log_info("压力测试 - 多连接并发")
    log_info("=" * 60)

    num_clients = min(args.count, 7)  # CH395F 最多支持 7 个并发连接
    log_info(f"创建 {num_clients} 个并发连接...")

    results = {}
    threads = []
    barrier = threading.Barrier(num_clients)

    # 创建并启动线程
    for i in range(num_clients):
        thread = threading.Thread(
            target=stress_test_thread,
            args=(i, args, results, barrier)
        )
        threads.append(thread)
        thread.start()
        time.sleep(0.1)  # 错开连接时间

    # 等待所有线程完成
    for thread in threads:
        thread.join()

    # 汇总结果
    total_success = sum(r[0] for r in results.values())
    total_fail = sum(r[1] for r in results.values())

    log_info("-" * 40)
    log_info("压力测试结果:")
    log_info(f"  并发连接数: {num_clients}")
    log_info(f"  每连接测试次数: {args.count}")
    log_info(f"  总成功: {total_success}")
    log_info(f"  总失败: {total_fail}")
    log_info(f"  成功率: {total_success/(total_success+total_fail)*100:.1f}%")

    if total_fail == 0:
        log_success("压力测试通过!")
    else:
        log_error("压力测试有失败!")


# ==================== DHCP 测试 ====================

DHCP_SERVER_PORT = 67
DHCP_CLIENT_PORT = 68
DHCP_DISCOVER = 1
DHCP_OFFER = 2
DHCP_REQUEST = 3
DHCP_ACK = 5
OPT_MESSAGE_TYPE = 53
OPT_SERVER_ID = 54
OPT_SUBNET_MASK = 1
OPT_ROUTER = 3
OPT_DNS = 6
OPT_LEASE_TIME = 51
OPT_END = 255


def _ip_to_bytes(ip):
    return bytes(map(int, ip.split('.')))


def _build_dhcp_pkt(op, xid, client_mac, yiaddr, siaddr, options):
    op_b = bytes([op])
    htype = b'\x01'
    hlen = b'\x06'
    hops = b'\x00'
    secs = b'\x00\x00'
    flags = b'\x00\x00'
    ciaddr = b'\x00\x00\x00\x00'
    giaddr = b'\x00\x00\x00\x00'
    chaddr = client_mac + b'\x00' * 10
    sname = b'\x00' * 64
    file = b'\x00' * 128
    magic = b'\x63\x82\x53\x63'
    return op_b + htype + hlen + hops + xid + secs + flags + ciaddr + yiaddr + siaddr + giaddr + chaddr + sname + file + magic + options


def dhcp_server_test(args):
    """DHCP Server 模式（DISCOVER → OFFER → REQUEST → ACK）"""
    log_info("=" * 60)
    log_info("DHCP Server 测试")
    log_info("=" * 60)

    server_ip = args.dhcp_iface
    offer_ip = args.ip
    subnet_mask = args.dhcp_mask
    router = args.dhcp_gw
    lease_time = 3600

    log_info(f"DHCP 服务器 IP: {server_ip}")
    log_info(f"待分配 IP: {offer_ip}")
    log_info(f"子网掩码: {subnet_mask}")
    log_info(f"网关: {router}")
    print()

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.bind(('0.0.0.0', DHCP_SERVER_PORT))
    sock.settimeout(60)

    log_info("DHCP 服务器已启动，等待 CH395F DISCOVER...")
    log_info("（请确保 MCU 端已启用 ENABLE_PHASE6_TESTS 并复位）")
    print()

    try:
        # Phase 1: DISCOVER → OFFER
        data, addr = sock.recvfrom(4096)
        if len(data) <= 250:
            log_error("收到无效 DHCP 包")
            return

        msg_type = data[242]
        if msg_type != DHCP_DISCOVER:
            log_error(f"预期 DISCOVER(1)，收到 {msg_type}")
            return

        xid = data[4:8]
        client_mac = data[28:34]
        mac_str = ':'.join(f'{b:02X}' for b in client_mac)
        log_success(f"收到 DHCP DISCOVER from {mac_str} (xid={xid.hex()})")

        # Build OFFER options
        options = b''
        options += struct.pack('BBB', OPT_MESSAGE_TYPE, 1, DHCP_OFFER)
        options += struct.pack('BB', OPT_SUBNET_MASK, 4) + _ip_to_bytes(subnet_mask)
        options += struct.pack('BB', OPT_ROUTER, 4) + _ip_to_bytes(router)
        options += struct.pack('BB', OPT_DNS, 4) + _ip_to_bytes(router)
        options += struct.pack('BB', OPT_SERVER_ID, 4) + _ip_to_bytes(server_ip)
        options += bytes([OPT_LEASE_TIME, 4]) + struct.pack('!I', lease_time)
        options += bytes([OPT_END])

        offer = _build_dhcp_pkt(2, xid, client_mac,
                                _ip_to_bytes(offer_ip),
                                _ip_to_bytes(server_ip),
                                options)

        # Try both global broadcast and subnet broadcast
        for bcast in ['255.255.255.255', '192.168.1.255']:
            sock.sendto(offer, (bcast, DHCP_CLIENT_PORT))
        log_success("发送 DHCP OFFER")

        # Phase 2: REQUEST → ACK（最多重试 8 次）
        sock.settimeout(5)
        phase2_ok = False
        for retry in range(8):
            try:
                data, addr = sock.recvfrom(4096)
                if len(data) <= 250:
                    continue
                msg_type = data[242]
                if msg_type == DHCP_REQUEST:
                    rx_xid = data[4:8]
                    rx_mac = data[28:34]
                    log_success(f"收到 DHCP REQUEST from {':'.join(f'{b:02X}' for b in rx_mac)}")

                    ack_options = b''
                    ack_options += struct.pack('BBB', OPT_MESSAGE_TYPE, 1, DHCP_ACK)
                    ack_options += struct.pack('BB', OPT_SUBNET_MASK, 4) + _ip_to_bytes(subnet_mask)
                    ack_options += struct.pack('BB', OPT_ROUTER, 4) + _ip_to_bytes(router)
                    ack_options += struct.pack('BB', OPT_DNS, 4) + _ip_to_bytes(router)
                    ack_options += struct.pack('BB', OPT_SERVER_ID, 4) + _ip_to_bytes(server_ip)
                    ack_options += bytes([OPT_LEASE_TIME, 4]) + struct.pack('!I', lease_time)
                    ack_options += bytes([OPT_END])

                    ack = _build_dhcp_pkt(2, rx_xid, rx_mac,
                                          _ip_to_bytes(offer_ip),
                                          _ip_to_bytes(server_ip),
                                          ack_options)
                    for bcast in ['255.255.255.255', '192.168.1.255']:
                        sock.sendto(ack, (bcast, DHCP_CLIENT_PORT))
                    log_success(f"发送 DHCP ACK → {offer_ip}")
                    log_success("DHCP 分配完成！")
                    phase2_ok = True
                    break
                elif msg_type == DHCP_DISCOVER:
                    # 重传 DISCOVER，重发 OFFER
                    xid = data[4:8]
                    client_mac = data[28:34]
                    options = b''
                    options += struct.pack('BBB', OPT_MESSAGE_TYPE, 1, DHCP_OFFER)
                    options += struct.pack('BB', OPT_SUBNET_MASK, 4) + _ip_to_bytes(subnet_mask)
                    options += struct.pack('BB', OPT_ROUTER, 4) + _ip_to_bytes(router)
                    options += struct.pack('BB', OPT_DNS, 4) + _ip_to_bytes(router)
                    options += struct.pack('BB', OPT_SERVER_ID, 4) + _ip_to_bytes(server_ip)
                    options += bytes([OPT_LEASE_TIME, 4]) + struct.pack('!I', lease_time)
                    options += bytes([OPT_END])
                    offer = _build_dhcp_pkt(2, xid, client_mac,
                                            _ip_to_bytes(offer_ip),
                                            _ip_to_bytes(server_ip),
                                            options)
                    for bcast in ['255.255.255.255', '192.168.1.255']:
                        sock.sendto(offer, (bcast, DHCP_CLIENT_PORT))
                    log_info(f"重发 DHCP OFFER (retry={retry+1})")
            except socket.timeout:
                log_warn(f"Phase 2 等待超时 (retry={retry+1})")
        if not phase2_ok:
            log_warn("DHCP REQUEST 未收到")

        print()

        # Phase 3: 连通性验证（MCU Phase 6 内置 HELLO 端口 = 60000）
        hello_port = args.port if args.port != 8080 else 60000
        log_info(f"验证 {offer_ip} 连通性（UDP HELLO → port {hello_port}）")
        sock.close()

        time.sleep(3)
        verify = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        verify.settimeout(2)
        phase3_ok = False
        for retry in range(5):
            try:
                verify.sendto(b"HELLO", (offer_ip, hello_port))
                data, addr = verify.recvfrom(64)
                if data == b"HELLO":
                    log_success(f"连通性验证通过：{addr[0]}:{addr[1]} 响应正常")
                    phase3_ok = True
                    break
                time.sleep(1)
            except (socket.timeout, ConnectionResetError, OSError):
                log_info(f"等待 UDP 响应... (retry={retry+1})")
                time.sleep(1)
        if not phase3_ok:
            log_warn("连通性验证超时（MCU 的 UDP 服务可能未运行或端口不正确）")
            log_info("提示：CH395F UDP echo 默认端口为 60000，请使用 --port 60000")

    except socket.timeout:
        log_error("等待 DHCP DISCOVER 超时（60s）")
    finally:
        sock.close()



# ==================== 数据包大小测试 ====================

def packet_size_test(args):
    """不同数据包大小测试"""
    log_info("=" * 60)
    log_info("数据包大小测试")
    log_info("=" * 60)

    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.settimeout(5)

    try:
        client_socket.connect((args.ip, args.port))
        log_success("连接成功")

        # 测试不同的数据包大小
        test_sizes = [1, 10, 100, 512, 1024, 2048, 4096]

        for size in test_sizes:
            try:
                test_data = generate_data(size)
                start_time = time.time()
                client_socket.send(test_data)

                received = b""
                while len(received) < size:
                    chunk = client_socket.recv(size - len(received))
                    if not chunk:
                        break
                    received += chunk

                elapsed = time.time() - start_time

                if received == test_data:
                    rate = size / elapsed / 1024 if elapsed > 0 else 0
                    log_success(f"大小 {size:5d} bytes: 成功, 速率 {rate:.1f} KB/s")
                else:
                    log_error(f"大小 {size:5d} bytes: 数据不匹配")
            except Exception as e:
                log_error(f"大小 {size:5d} bytes: 错误 - {e}")

            time.sleep(0.1)

    except Exception as e:
        log_error(f"测试错误: {e}")
    finally:
        client_socket.close()


# ==================== 主程序 ====================

def main():
    parser = argparse.ArgumentParser(
        description='CH395F BSD Socket API 测试程序',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  %(prog)s tcp_client --ip 192.168.1.100 --port 8080
  %(prog)s tcp_server --port 8080
  %(prog)s stress --ip 192.168.1.100 --port 8080 --count 5
  %(prog)s dhcp_server --dhcp-iface 192.168.1.2 --ip 192.168.1.100
        """
    )

    parser.add_argument('mode', choices=['tcp_server', 'tcp_client', 'udp_server', 'udp_client',
                                          'tcp_echo', 'udp_echo', 'stress', 'packet_size',
                                          'dhcp_server'],
                        help='测试模式')
    parser.add_argument('--ip', default='192.168.1.100', help='目标 IP 地址 / DHCP 待分配 IP')
    parser.add_argument('--port', type=int, default=8080, help='端口号')
    parser.add_argument('--count', type=int, default=10, help='测试次数')
    parser.add_argument('--size', type=int, default=1024, help='数据包大小')
    parser.add_argument('--timeout', type=int, default=180, help='接受连接超时（秒，默认180）')
    parser.add_argument('--dhcp-iface', default='192.168.1.2', help='DHCP 服务器网卡 IP')
    parser.add_argument('--dhcp-mask', default='255.255.255.0', help='DHCP 分配的子网掩码')
    parser.add_argument('--dhcp-gw', default='192.168.1.1', help='DHCP 分配的网关')

    args = parser.parse_args()

    print()
    print(Colors.BOLD + "=" * 60 + Colors.END)
    print(Colors.BOLD + "  CH395F BSD Socket API 测试程序" + Colors.END)
    print(Colors.BOLD + "=" * 60 + Colors.END)
    print()

    try:
        if args.mode == 'tcp_server':
            tcp_server_test(args)
        elif args.mode == 'tcp_client':
            tcp_client_test(args)
        elif args.mode == 'udp_server':
            udp_server_test(args)
        elif args.mode == 'udp_client':
            udp_client_test(args)
        elif args.mode == 'tcp_echo':
            tcp_echo_server_test(args)
        elif args.mode == 'udp_echo':
            udp_echo_server_test(args)
        elif args.mode == 'stress':
            stress_test(args)
        elif args.mode == 'packet_size':
            packet_size_test(args)
        elif args.mode == 'dhcp_server':
            dhcp_server_test(args)
    except KeyboardInterrupt:
        log_warn("用户中断")
    except Exception as e:
        log_error(f"未预期的错误: {e}")
        import traceback
        traceback.print_exc()


if __name__ == '__main__':
    main()
