sing-box icon indicating copy to clipboard operation
sing-box copied to clipboard

TUN模式下的TCP首包延迟增加

Open 1F357 opened this issue 1 year ago • 5 comments

操作系统

Windows

系统版本

10 LTSC

安装类型

宣传使用 sing-box 的第三方图形客户端程序 (Windows)

如果您使用图形客户端程序,请提供该程序版本。

V2RayN V6.55

版本

Environment: go1.22.6 windows/amd64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api
Revision: 3066dfe3b31c0d436766047ab6c363be5c60ff53
CGO: disabled

描述

在启用v2RayN的TUN模式后,在建立TCP连接后,发送第一个数据包增加了约200ms-300ms的延迟。测试机器的CPU是1225 v5,理论上不存在CPU瓶颈。

v2RayN自带的绕过大陆和黑名单模式均会遇到问题。

测试服务器在境内,localhost测试没有延迟问题,貌似是在公网上才会遇到

未启用TUN模式:

TCP handshake time: 24.30 ms
Packet 1 round-trip time: 23.83 ms
Received from server: Message received
Packet 2 round-trip time: 23.50 ms
Received from server: Message received
Packet 3 round-trip time: 23.80 ms
Received from server: Message received
Average round-trip time for 3 packets: 23.71 ms

在启用TUN模式后:

TCP handshake time: 0.64 ms
Packet 1 round-trip time: 322.09 ms
Received from server: Message received
Packet 2 round-trip time: 19.89 ms
Received from server: Message received
Packet 3 round-trip time: 19.85 ms
Received from server: Message received
Average round-trip time for 3 packets: 120.61 ms

类似的,建立ssh连接也遇到了类似的延迟增加问题。

SSH handshake time: 217.55 ms
SSH handshake time: 233.25 ms
SSH handshake time: 230.96 ms
SSH handshake time: 246.99 ms
SSH handshake time: 216.03 ms
SSH handshake time: 224.81 ms
SSH handshake time: 234.40 ms
SSH handshake time: 236.22 ms
# 开启TUN后
SSH handshake time: 463.93 ms
SSH handshake time: 469.41 ms
SSH handshake time: 451.46 ms
SSH handshake time: 479.30 ms
SSH handshake time: 458.75 ms
SSH handshake time: 484.66 ms

通过后续时间戳的比较,可以确认是客户端发向服务端的第一个tcp包遇到了延迟,服务器返回的数据包是正常的。在TCP握手后time.sleep(1),发送的第一个数据包没有遇到延迟增加的问题。

使用相同的脚本,把客户端发送的数据从 MESSAGE = b"hello" 改成 MESSAGE = b"GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n" 延迟问题就解决了

在macos(Sequoia,MacBook air M1)上的Shadowrocket,即便开启“直连”,也遇到一模一样的的延迟增加问题,非常诡异,同样也是把MESSAGE改成HTTP头后问题解决。

在macOS和windows的Mihomo core的TUN模式均没有遇到延迟问题,延迟一切正常。

重现方式

GPT写的几个脚本 tcp_test_client.py

import socket
import time

# Server configuration
HOST = 'x.x.x.x'  
PORT = 65432           

# Dummy data to send
MESSAGE = b"hello"

def main():
    while True:
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                # Measure TCP handshake time
                start_handshake_time = time.perf_counter()
                s.connect((HOST, PORT))
                end_handshake_time = time.perf_counter()

                tcp_handshake_time = (end_handshake_time - start_handshake_time) * 1000  # Convert to milliseconds
                print(f"TCP handshake time: {tcp_handshake_time:.2f} ms")

                # Send multiple packets to measure round-trip time stability
                total_round_trip_time = 0
                num_packets = 3
                # time.sleep(1)
                for i in range(num_packets):
                    try:
                        # Measure time for sending the packet and receiving the response
                        start_send_time = time.perf_counter()
                        s.sendall(MESSAGE)

                        # Wait for the server response (receive only the first packet)
                        data = s.recv(1024)  # Receiving first response packet

                        end_receive_time = time.perf_counter()

                        packet_time = (end_receive_time - start_send_time) * 1000  # Convert to milliseconds
                        total_round_trip_time += packet_time
                        print(f"Packet {i+1} round-trip time: {packet_time:.2f} ms")

                        # Print the received data
                        if data:
                            print(f"Received from server: {data.decode('utf-8')}")
                        else:
                            print("No data received from server.")

                    except socket.error as e:
                        print(f"Error during communication: {e}")
                        break

                # Calculate average round-trip time
                average_round_trip_time = total_round_trip_time / num_packets
                print(f"Average round-trip time for {num_packets} packets: {average_round_trip_time:.2f} ms")
                break

        except Exception as e:
            print(f"Failed to connect to {HOST}:{PORT}: {e}")
            time.sleep(1)  # Wait before retrying

while True:
    main()
    time.sleep(5)

tcp_test_server.py

import socket

# Server configuration
HOST = '0.0.0.0'  # Listen on all interfaces
PORT = 65432      # Port to listen on

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, PORT))
        s.listen()

        print(f"Server listening on {HOST}:{PORT}")

        while True:
            conn, addr = s.accept()
            with conn:
                print(f"Connected by {addr}")
                
                while True:
                    # Receive data from the client
                    data = conn.recv(1024)
                    if not data:
                        print("Connection closed by client.")
                        break  # Exit loop if the client has closed the connection
                    
                    print(f"Received: {data.decode('utf-8')}")
                
                    # Send acknowledgment back to the client
                    conn.sendall(b"Message received")
                    print("Acknowledgment sent to client.")

if __name__ == "__main__":
    main()

ssh_test.py

import paramiko
import time

def measure_ssh_handshake_time(host, port=22, username=None, password=None, pkey=None):
    # Create a new SSH client
    client = paramiko.SSHClient()
    # Automatically add untrusted hosts (make sure okay for security policy in real usage)
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
    # Measure the time it takes to perform the SSH handshake
    try:
        start_time = time.time()
        
        # Connect to the SSH server (without actually opening a session or executing commands)
        client.connect(hostname=host, port=port, username=username, password=password, pkey=pkey, timeout=10)
        
        # Time when the server's certificate is received and validated
        end_time = time.time()
        
        handshake_time = (end_time - start_time) * 1000  # Convert to milliseconds
        print(f"SSH handshake time: {handshake_time:.2f} ms")
        
    except Exception as e:
        print(f"Failed to connect to {host}:{port} - {e}")
    
    finally:
        # Close the connection
        client.close()

if __name__ == "__main__":
    while True:
        # Specify the host and port you want to connect to
        HOST = 'x.x.x.x'  # Replace with the actual server address
        PORT = 22  # Default SSH port, change if necessary
        
        # Optionally, you can provide a username and password or a private key
        USERNAME = 'xxx'
        PASSWORD = 'xxxx'  # Alternatively, set to None if using key-based authentication
        PKEY = None  # Replace with paramiko.RSAKey object if using key-based authentication
        
        measure_ssh_handshake_time(HOST, PORT, USERNAME, PASSWORD, PKEY)
        time.sleep(1)

日志

No response

支持我们

完整性要求

  • [X] 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
  • [X] 我保证提供了可以在本地重现该问题的服务器、客户端配置文件与流程,而不是一个脱敏的复杂客户端配置文件。
  • [X] 我保证提供了可用于重现我报告的错误的最简配置,而不是依赖远程服务器、TUN、图形界面客户端或者其他闭源软件。
  • [x] 我保证提供了完整的配置文件与日志,而不是出于对自身智力的自信而仅提供了部分认为有用的部分。

1F357 avatar Aug 26 '24 19:08 1F357

我们不是广告软件的客服,请反馈 sing-box 的问题并排除其他程序造成的影响。

nekohasekai avatar Aug 27 '24 02:08 nekohasekai

我们不是广告软件的客服,请反馈 sing-box 的问题并排除其他程序造成的影响。

谢谢回复,但是 v2rayN的TUN模式使用了sing-box,我单独测试sing-box仍能复现这个问题,于是才来的这里提出的issue。 测试的远端服务器在腾讯云北京。

TCP handshake time: 0.45 ms
Packet 1 round-trip time: 356.18 ms
Received from server: Message received
Packet 2 round-trip time: 24.86 ms
Received from server: Message received
Packet 3 round-trip time: 24.97 ms
Received from server: Message received
Average round-trip time for 3 packets: 135.34 ms

以下是我使用的sing-box配置文件,我使用sing-box的经验不多,如果有配置错误烦请指出🙏。

{
    "log": {
      "level": "warn",
      "timestamp": true
    },
    "dns": {
      "servers": [
        {
          "tag": "local",
          "address": "223.5.5.5",
          "strategy": "ipv4_only",
          "detour": "direct"
        }
      ],
      "final": "local"
    },
    "inbounds": [
      {
        "type": "tun",
        "tag": "tun-in",
        "interface_name": "singbox_tun",
        "inet4_address": "172.19.0.1/30",
        "inet6_address": "fdfe:dcba:9876::1/126",
        "mtu": 9000,
        "auto_route": true,
        "strict_route": true,
        "stack": "gvisor",
        "sniff": true
      }
    ],
    "outbounds": [
      {
        "type": "direct",
        "tag": "direct"
      },
      {
        "type": "block",
        "tag": "block"
      },
      {
        "type": "dns",
        "tag": "dns_out"
      }
    ],
    "route": {
      "auto_detect_interface": true,
      "rules": [
        {
          "outbound": "direct",
          "port_range": [
            "0:65535"
          ]
        }
      ]
    }
  }

1F357 avatar Aug 27 '24 08:08 1F357

sing-box version 1.9.4

Environment: go1.22.6 windows/amd64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api
Revision: 3066dfe3b31c0d436766047ab6c363be5c60ff53
CGO: disabled
sing-box version 1.10.0-beta.4

Environment: go1.23.0 windows/amd64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api
Revision: 68ec00aaa5a20f1adbf9dfb56804ee2e0c4d0671
CGO: disabled

两个版本均可复现。

日志如下,管理员身份运行。

PS C:\Users\_\Desktop\sing_box> .\sing-box.exe -c .\config.json run
+0800 2024-08-27 16:51:03 [36mINFO[0m router: updated default interface 以太网, index 7
+0800 2024-08-27 16:51:03 [36mINFO[0m inbound/tun[tun-in]: started at singbox_tun
+0800 2024-08-27 16:51:03 [36mINFO[0m sing-box started (0.588s)
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;208m81714368[0m 0ms] inbound/tun[tun-in]: inbound packet connection from 172.19.0.1:52692
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;208m81714368[0m 3ms] inbound/tun[tun-in]: inbound packet connection to 172.19.0.2:53
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;208m81714368[0m 25ms] outbound/direct[direct]: outbound packet connection
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;131m3542776947[0m 0ms] inbound/tun[tun-in]: inbound packet connection from 172.19.0.1:56624
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;131m3542776947[0m 1ms] inbound/tun[tun-in]: inbound packet connection to 172.19.0.2:53
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;131m3542776947[0m 3ms] outbound/direct[direct]: outbound packet connection
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;104m3538114904[0m 0ms] inbound/tun[tun-in]: inbound packet connection from 172.19.0.1:57973
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;104m3538114904[0m 0ms] inbound/tun[tun-in]: inbound packet connection to 172.19.0.2:53
+0800 2024-08-27 16:51:04 [36mINFO[0m [[38;5;104m3538114904[0m 2ms] outbound/direct[direct]: outbound packet connection
+0800 2024-08-27 16:51:07 [36mINFO[0m [[38;5;69m4175692085[0m 0ms] inbound/tun[tun-in]: inbound connection from 172.19.0.1:57348
+0800 2024-08-27 16:51:07 [36mINFO[0m [[38;5;69m4175692085[0m 0ms] inbound/tun[tun-in]: inbound connection to x.x.x.x:65432
+0800 2024-08-27 16:51:07 [36mINFO[0m [[38;5;69m4175692085[0m 304ms] outbound/direct[direct]: outbound connection to x.x.x.x:65432

1F357 avatar Aug 27 '24 08:08 1F357

从日志最后一行来看,我个人猜测可能是分流时耗费了时间,发送的 MESSAGE从hello 改成 MESSAGE = b"GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n" 之类的类似的http头,延迟会大幅降低 +0800 2024-08-27 16:57:27 [36mINFO[0m [[38;5;50m4049345017[0m 23ms] outbound/direct[direct]: outbound connection to x.x.x.x7:65432 ssh之类的连接延迟均在200-300ms左右,貌似是只有http头能在30ms内 但个人不会写golang,还在艰难地研究中,抱歉

1F357 avatar Aug 27 '24 08:08 1F357

对于服务器先发协议,是预期行为;对于其他类型的连接,应已修复部分问题。

nekohasekai avatar Sep 14 '24 02:09 nekohasekai