Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
4 of 5 tasks
1F357 opened this issue Aug 26, 2024 · 5 comments
Open
4 of 5 tasks

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

1F357 opened this issue Aug 26, 2024 · 5 comments
Labels
bug Something isn't working

Comments

@1F357
Copy link

1F357 commented Aug 26, 2024

操作系统

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

支持我们

完整性要求

  • 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
  • 我保证提供了可以在本地重现该问题的服务器、客户端配置文件与流程,而不是一个脱敏的复杂客户端配置文件。
  • 我保证提供了可用于重现我报告的错误的最简配置,而不是依赖远程服务器、TUN、图形界面客户端或者其他闭源软件。
  • 我保证提供了完整的配置文件与日志,而不是出于对自身智力的自信而仅提供了部分认为有用的部分。
@nekohasekai nekohasekai added need more information not following template Necessary information is not provided or is incomplete from end user An end user is asking a developer to fix their problem for free labels Aug 27, 2024
@nekohasekai
Copy link
Member

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

@nekohasekai nekohasekai closed this as not planned Won't fix, can't repro, duplicate, stale Aug 27, 2024
@1F357
Copy link
Author

1F357 commented Aug 27, 2024

我们不是广告软件的客服,请反馈 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
Copy link
Author

1F357 commented Aug 27, 2024

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
Copy link
Author

1F357 commented Aug 27, 2024

从日志最后一行来看,我个人猜测可能是分流时耗费了时间,发送的
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,还在艰难地研究中,抱歉

@nekohasekai nekohasekai reopened this Aug 27, 2024
@nekohasekai nekohasekai added bug Something isn't working and removed need more information not following template Necessary information is not provided or is incomplete from end user An end user is asking a developer to fix their problem for free labels Aug 27, 2024
@nekohasekai
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

7 participants
@nekohasekai @1F357 and others