通过 localhost 环回使用 TCP (SOCK_STREAM) 进行重传/乱序数据包(约 1 MB)写入/读取 — ACK 丢失

通过 localhost 环回使用 TCP (SOCK_STREAM) 进行重传/乱序数据包(约 1 MB)写入/读取 — ACK 丢失

我在使用环回接口时看到了重传/乱序数据包,并且已经在谷歌上搜索了几天,试图确定我是否应该对此感到惊讶(我就是)或者是否有其他人看到它,但我还没有没有找到任何答案。如果我错过了一些东西,我很抱歉。有人提到了环回规范,但我找不到它。有谁知道环回接口规范吗?

由于丢失 ACK 导致的重传示例:

/root
root@cmsstor911 :^| tshark -nr/tmp/t.tcpdump -te | egrep -i 'retrans|out.of.order' -B3 -A2 
Running as user "root" and group "root". This could be dangerous.
 54 1582904297    127.0.0.1 -> 127.0.0.1    TCP 66 7001 > 45529 [ACK] Seq=449 Ack=917399 Win=2752512 Len=0 TSval=3255514024 TSecr=3255514024
 55 1582904297    127.0.0.1 -> 127.0.0.1    TCP 65549 45529 > 7001 [ACK] Seq=982882 Ack=449 Win=49152 Len=65483 TSval=3255514024 TSecr=3255514024
 56 1582904297    127.0.0.1 -> 127.0.0.1    TCP 278 45529 > 7001 [PSH, ACK] Seq=1048365 Ack=449 Win=49152 Len=212 TSval=3255514024 TSecr=3255514024
 57 1582904297    127.0.0.1 -> 127.0.0.1    TCP 278 [TCP Retransmission] 45529 > 7001 [PSH, ACK] Seq=1048365 Ack=449 Win=49152 Len=212 TSval=3255514034 TSecr=3255514024
 58 1582904297    127.0.0.1 -> 127.0.0.1    TCP 78 7001 > 45529 [ACK] Seq=449 Ack=1048577 Win=2686976 Len=0 TSval=3255514034 TSecr=3255514024 SLE=1048365 SRE=1048577
 59 1582904297    127.0.0.1 -> 127.0.0.1    TCP 178 7001 > 45529 [PSH, ACK] Seq=449 Ack=1048577 Win=2686976 Len=112 TSval=3255514224 TSecr=3255514024

很难让 tshark 很好地打印微秒,但似乎正在发生的情况是数据包 56 是一段时间内的最后一个数据包(在我的应用程序中)。确认之前发生了 2 个数据包,因此应该发送一个,但事实并非如此。大约。 9.056 毫秒过去,发送方决定重传。接收方及时发送SACK。

这是我正在做的一件事(在几个现代 Linux 内核上尝试过,包括 5.5.4):

sudo tcpdump -s78 -wt.tcpdump -ilo port 7001 & tcpdump_pid=$!; \
taskset -c 1 ./tcp_loopback.py -s -b1048576 & sleep .5; taskset -c 2 ./tcp_loopback.py -c -b1048576 --count=8192; \
sudo kill $tcpdump_pid; \
tshark -r t.tcpdump | egrep -i 'retrans|out.of.order' | tail

tcp_loopback.py 脚本可在 home.fnal.gov/~ron/tcp_loopback.py 上找到

服务器部分(-s)的核心是:

    sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
    sock.bind( ('127.0.0.1', port) )
    sock.listen( 4 )
    sockconn,address = sock.accept()
    while 1:
        data = sockconn.recv(bs)
        if opargs['-v']=='': print('received: '+str(len(data)))
        if len(data) == 0:
            if opargs['-v']=='': print('0 data, closing')
            break

客户端部分 (-s) 的核心是:

    sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    sock.connect( ('127.0.0.1',port) )
    for xx in range(cnt): sock.send( '*'*bs )

重传的概率似乎随着较大(即 2M、4M)写入/读取而增加。

我读过(例如 Documentation/networking/scaling.rst)有关与不同内核上的调度相关的无序问题,因此使用上面的“任务集”。

我相信重传是真实的,因为我在 tcpdump 中看到段传输了两次。另外,我有一个自定义应用程序,它使用 NETLINK_SOCK_DIAG 套接字在传输正在进行时获取重传信息;我在使用它时看到了 localhost/loopback 的重传。似乎绝大多数(也许全部)传输都是由无序现象引起的,因此问题可能确实是:是否有任何技巧可以发挥(除了任务集之外)来确保使用时的数据包顺序环回接口?

使用环回,我真的不知道我是否正在查看发送处理无序发送的内容或接收处理接收无序的内容。我的最终目标是在 100 Gi、高拥塞(多对一)环境中建立低延迟节点间传输的基线。我开发了一个使用“调试套接字”来获取重传信息的应用程序,我很惊讶地看到本地主机上的重传。

任何人都可以帮助我了解发生了什么以及是否有任何旋钮(例如 sysctl)可以转动以消除重传,同时仍然最大化数据速率?

谢谢,罗恩

PS 当我这样做时,我最终也看到了这个问题:

while true;do
 sudo tcpdump -s78 -w/tmp/t.tcpdump -ilo port 7001 & tcpdump_pid=$!
 ncat -4l localhost 7001 > /dev/null & ncat_pid=$!
 sleep .5
 dd if=/dev/zero bs=1048576 count=2048 | ncat localhost 7001
 sudo kill $tcpdump_pid
 tshark -r /tmp/t.tcpdump | egrep -i 'retrans|out.of.order' && break
done

输出示例:

/home/ron/notes
ron@ronlap77 :^) sudo tcpdump -s78 -wt.tcpdump -ilo port 7001 & tcpdump_pid=$!; \
> taskset -c 1 ./tcp_loopback.py -s -b1048576 & sleep .5; taskset -c 2 ./tcp_loopback.py -c -b1048576 --count=8192; \
> sudo kill $tcpdump_pid; \
> tshark -r t.tcpdump | egrep -i 'retrans|out.of.order' | tail
[1] 29066
[2] 29067
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 78 bytes
191379 packets captured
383230 packets received by filter
0 packets dropped by kernel
[2]+  Done                    taskset -c 1 ./tcp_loopback.py -s -b1048576
189947   1.092534    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4203412074 Ack=1 Win=65536 Len=65483 TSval=2693245609 TSecr=2693245609
189948   1.092535    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4203477557 Ack=1 Win=65536 Len=65483 TSval=2693245609 TSecr=2693245609
189949   1.092536    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4203543040 Ack=1 Win=65536 Len=65483 TSval=2693245609 TSecr=2693245609
189950   1.092536    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4203608523 Ack=1 Win=65536 Len=65483 TSval=2693245609 TSecr=2693245609
189951   1.092537    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4203674006 Ack=1 Win=65536 Len=65483 TSval=2693245609 TSecr=2693245609
189962   1.092594    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Spurious Retransmission] 46988 → 7001 [ACK] Seq=4203674006 Ack=1 Win=65536 Len=65483 TSval=2693245609 TSecr=2693245609
190115   1.093456    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4211997131 Ack=1 Win=65536 Len=65483 TSval=2693245610 TSecr=2693245610
190116   1.093457    127.0.0.1 → 127.0.0.1    TCP 65549 [TCP Out-Of-Order] 46988 → 7001 [ACK] Seq=4212062614 Ack=1 Win=65536 Len=65483 TSval=2693245610 TSecr=2693245610
190117   1.093457    127.0.0.1 → 127.0.0.1    TCP 1762 [TCP Out-Of-Order] 46988 → 7001 [PSH, ACK] Seq=4212128097 Ack=1 Win=65536 Len=1696 TSval=2693245610 TSecr=2693245610
190138   1.093547    127.0.0.1 → 127.0.0.1    TCP 1762 [TCP Out-Of-Order] 46988 → 7001 [PSH, ACK] Seq=4212128097 Ack=1 Win=65536 Len=1696 TSval=2693245610 TSecr=2693245610
[1]+  Done                    sudo tcpdump -s78 -wt.tcpdump -ilo port 7001
--2020-02-25_11:27:17--

相关内容