Skip to content

Commit

Permalink
Add changes
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] committed Mar 18, 2024
1 parent 25c1d82 commit 6b88b93
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 10 deletions.
2 changes: 1 addition & 1 deletion notes
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-aarch64-compile.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-10-01
updated: 2024-02-29
updated: 2024-03-18
title: 关于交叉编译的一些记录
description: 编译的话文档有很多,这里记录一下自己交叉编译的一些命令。
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-data-mig.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-06-22
updated: 2024-02-29
updated: 2024-03-18
title: linux 服务器数据迁移
description: 前段时间把社团服务器上的数据进行了迁移,这里记录一下大概的迁移方式。管道的妙用。ssh 配合 tar 直接将文件传到我本机电脑:附上 exclude.txt 文件:
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-emulate-aarch64.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-09-30
updated: 2024-02-29
updated: 2024-03-18
title: QEMU-aarch64 启动!(使用 QEMU 运行 aarch64 虚拟机)
description: qemu-emulators-full 会提供支持架构的全系统模拟( 如 qemu-system-aarch64 或 qemu-x86_64 ) edk2-aarch64 提供 uefi 固件
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-kernel-hashtable.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-08-17
updated: 2024-02-29
updated: 2024-03-18
title: linux kernel hashtable
description: 记录一下自己对 linux kernel 中的 hashtable 实现的理解(当然是查的资料)。因为最近写的 objtool 就有很多地方用到 hashtable,不记录一下每次去看都很烦。
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-nfs-vlc.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-08-31
updated: 2024-02-29
updated: 2024-03-18
title: vlc 使用 nfs 访问 linux
description: 最近在 vlc 上将电脑的一些文件挂载到手机上,这样手机就可以看电脑上的番了。前置条件:下载安装 nfs-utils将 vlc 需要访问的文件挂载到 /srv/nfs/ 目录中:修改 /etv/exports 配置,以保证安全性,这里只允许读:
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-qemu-nbd.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-08-17
updated: 2024-02-29
updated: 2024-03-18
title: 使用 qemu-nbd 对虚拟机进行扩容
description: 或者直接指定虚拟磁盘的大小的缩写:它是一种允许一台机器访问另一台机器上的块设备的协议。在 linux 中,这一功能由 nbd 模块实现,需要加载该模块:
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/linux-vm-install-kernel.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-09-30
updated: 2024-02-29
updated: 2024-03-18
title: 给虚拟机替换内核
description: 因为开源之夏项目写的是 arm64 架构上的内核,本机使用的是 x86 ,所以只能开虚拟机进行测试。我之前的笨方法安装是将所有 kernel 根目录的文件全都使用 rsync 同步到虚拟机,然后在虚拟机里面使用 make modules_install install 和 make install。简直不要太笨。每次修改代码都要 rsync 一次。后来问了一下开源之夏的导师,才想起来可以将虚拟机镜像挂载然后在本机安装内核进虚拟机镜像中。在这里记录一下步骤。挂载虚拟机镜像安装 module安装内核
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/stack-unwinding.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-06-23
updated: 2024-02-29
updated: 2024-03-18
title: stack unwinding
description: 将 linux 中的 stack unwinding 的一些概念进行梳理。
tags:
Expand Down
2 changes: 1 addition & 1 deletion source/_posts/linux/wayland-wemeet-screen-share.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: 2023-08-31
updated: 2024-02-29
updated: 2024-03-18
title: wayland 腾讯会议共享屏幕
description: 前置条件:开启虚拟摄像头:在 obs 里面就可以打开虚拟摄像头录取屏幕然后在腾讯会议共享屏幕了。遇到开启虚拟摄像头 obs 卡住的话重启+重新创建虚拟摄像头。
tags:
Expand Down
156 changes: 156 additions & 0 deletions source/_posts/tcp/TCP_3_way_handshaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
date: 2024-03-18
title: TCP 三次握手
description: 在网络通信中,TCP(Transmission Control Protocol)是一种可靠的传输协议,而TCP三次握手是建立TCP连接时的重要过程之一。TCP 三次握手,包括三个步骤:
tags:
- tcp
- network

categories:
- [tcp]
---

最近在看某个项目的一个功能:[network-lock](https://www.criu.org/TCP_connection#Checkpoint_and_restore_TCP_connection), 感觉还挺有意思的,于是写了一个 TCP Demo 来测试这个功能,但是在我手动将 server 关闭的时候发现不能马上使用上次 server 监听的端口,而是跟我说端口已被占用,有些好奇,于是就有了以下的一些折腾。顺便在这里记录一下关于 TCP 三次握手的笔记。顺带 `tcpdump` 分析。

## 介绍

在网络通信中,TCP(Transmission Control Protocol)是一种可靠的传输协议,而TCP三次握手是建立TCP连接时的重要过程之一。

## 概述

TCP 三次握手,包括三个步骤:

1. 客户端发送SYN请求:客户端向服务器发送一个SYN(同步)标志的数据包,表明它想要建立连接。

2. 服务器响应ACK和SYN:服务器接收到客户端的SYN请求后,向客户端发送一个ACK(确认)数据包,表示已收到客户端的请求,并发送自己的SYN标志,以示同意建立连接。

3. 客户端发送ACK:客户端接收到服务器的ACK和SYN后,向服务器发送一个ACK确认数据包,表示已收到服务器的响应,连接建立完成。

## 代码示例与 tcpdump 分析

下面是一个 client 和 server 建立 TCP 连接的代码。我最开始是用 C 写的,但是后面发现 Python 写起来舒服多了。

这个 Server 会监听 localhost 的 8880 端口。
`Server.py`:

```py
import socket


server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


server_address = ('localhost', 8880)
print('Starting up on {} port {}'.format(*server_address))
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(server_address)

server_socket.listen(1)

while True:
print('Waiting for a connection...')
connection, client_address = server_socket.accept()
try:
print('Connection from', client_address)

while True:
data = connection.recv(1024)
print('Received:', data.decode())

if not data:
print('No data received from', client_address)
break

finally:
connection.close()
```

这个 Client 会与 Server 建立连接并每隔 2s 发送当前时间给 Server 。
`Client.py`:

```py
import socket
import time

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

server_address = ('localhost', 8880)
print('Connecting to {} port {}'.format(*server_address))
client_socket.connect(server_address)

try:
while True:
message = 'Hello, server! Time is {}'.format(time.ctime())
print('Sending:', message)
client_socket.sendall(message.encode())
time.sleep(2)

finally:
print('Closing the connection')
client_socket.close()
```

运行这两个代码,使用 `tcpdump` 来监听 8880 端口的 tcp 数据包:

三次握手:

```
$ tcpdump -i lo -n port 8880 --absolute-tcp-sequence-numbers
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
16:18:18.333865 IP 127.0.0.1.57830 > 127.0.0.1.8880: Flags [S], seq 1857514943, win 33280, options [mss 65495,sackOK,TS val 3851230309 ecr 0,nop,wscale 7], length 0
16:18:18.333874 IP 127.0.0.1.8880 > 127.0.0.1.57830: Flags [S.], seq 3256784619, ack 1857514944, win 33280, options [mss 65495,sackOK,TS val 3851230309 ecr 3851230309,nop,wscale 7], length 0
16:18:18.333880 IP 127.0.0.1.57830 > 127.0.0.1.8880: Flags [.], ack 3256784620, win 260, options [nop,nop,TS val 3851230309 ecr 3851230309], length 0
16:18:18.333910 IP 127.0.0.1.57830 > 127.0.0.1.8880: Flags [P.], seq 1857514944:1857514991, ack 3256784620, win 260, options [nop,nop,TS val 3851230309 ecr 3851230309], length 47
16:18:18.333912 IP 127.0.0.1.8880 > 127.0.0.1.57830: Flags [.], ack 1857514991, win 260, options [nop,nop,TS val 3851230309 ecr 3851230309], length 0
```

> PS: tcpdump 的参数说明:
>
> i lo: 监听 lo 网卡
> n: 不解析 IP 地址
> port 8880: 只监听 8880 端口的数据包
> absolute-tcp-sequence-numbers: 显示绝对的 TCP 序列号,如果不加这个参数,tcpdump 会显示相对的序列号。
`tcpdump` 输出的 Flags 字段解释:

[.] - ACK (Acknowledgment)
[S] - SYN (Start Connection)
[P] - PSH (Push Data)
[F] - FIN (Finish Connection)
[R] - RST (Reset Connection)
[S.] - SYN-ACK (SynAcK Packet)

可以看到,Client 发送了一个 SYN 数据包,Server 回复了一个 SYN+ACK 数据包,Client 再回复一个 ACK 数据包,三次握手完成。

seq 和 ack 是 TCP 数据包中的两个重要字段,seq 表示数据包的序列号,ack 表示确认号。SYN 数据包的 seq 是随机的,SYN+ACK 数据包的 seq 是随机的,ack 是 SYN 数据包的 seq+1,ACK 数据包的 seq 是 SYN+ACK 数据包的 ack,ack 是 SYN+ACK 数据包的 seq+1。

seq 和 ack 的作用是用来保证数据包的可靠传输。TCP 通过 seq 和 ack 来保证数据包的有序传输和可靠接收。

三次挥手和四次挥手:

```
16:21:10.284200 IP 127.0.0.1.57830 > 127.0.0.1.8880: Flags [F.], seq 1857515790, ack 3256784620, win 260, options [nop,nop,TS val 3851402259 ecr 3851400314], length 0
16:21:10.284297 IP 127.0.0.1.8880 > 127.0.0.1.57830: Flags [F.], seq 3256784620, ack 1857515791, win 260, options [nop,nop,TS val 3851402259 ecr 3851402259], length 0
16:21:10.284308 IP 127.0.0.1.57830 > 127.0.0.1.8880: Flags [.], ack 3256784621, win 260, options [nop,nop,TS val 3851402259 ecr 3851402259], length 0
```
> 这里三次挥手是 Linux 默认开启 [TCP 延迟确认机制](https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment),而 RFC795 中是四次挥手:https://www.rfc-editor.org/rfc/rfc793#section-3.5
> 关于 TCP 延迟确认机制,可以使用 `TCP_QUICKACK` 关闭:https://www.man7.org/linux/man-pages/man7/tcp.7.html
四次挥手的过程:

1. Client 发送一个 FIN 数据包,表示不再发送数据了。
2. Server 收到 FIN 数据包后,回复一个 ACK 数据包,表示收到了 Client 的 FIN 数据包。
3. Server 发送一个 FIN 数据包,表示不再发送数据了。
4. Client 收到 FIN 数据包后,回复一个 ACK 数据包,表示收到了 Server 的 FIN 数据包。

三次挥手的过程只是将 FIN 与 ACK 数据包一同发送。

首先发出 FIN 的一侧,如果给“对侧”的 FIN 响应了 ACK,那么就会超时等待 2 *MSL 时间,然后关闭连接。在这段超时等待时间内,本地的端口不能被新连接使用;避免延时的包的到达与随后的新连接相混淆。RFC793 定义了 MSL 为 2 分钟,Linux 设置成了 [30s](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/ipv4/tcp.c?h=linux-6.8.y#n2863) 。参数 tcp_max_tw_buckets 控制并发的 TIME_WAIT 的数量,默认值是 180000,如果超限,那么,系统会把多的 TIME_WAIT 状态的连接给 destory 掉,然后在日志里打一个警告(如:time wait bucket table overflow)

这也就是手动将 server 进程杀掉之后,端口仍被占用的原因。TCP 连接关闭之后,不会马上释放这个 socket ,之后任何想将新 socket 绑定到相同的 地址 和端口的操作都会失败,直到旧的 socket 关闭为止。

> 可以使用 `SO_REUSEADDR` 解决这个问题。
> https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ
126 changes: 126 additions & 0 deletions source/_posts/tcp/tcp-fast-open.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
date: 2024-03-18
title: TCP Fast Open
description: 折腾 TCP 的同时看到了 TCP Fast Open 这个有趣的参数,它是对 TCP 的优化,无需等待 3 次握手,应用程序就可以通过 TCP 发送数据。正常 TCP 建立连接过程:当前 TCP 实现的问题是,只有在连接发起方收到来自对等 TCP 的 ACK(确认)段后,才能在连接上交换数据。也就是说,只有在三次握手的第三步(发起方发送的ACK报文段)中,数据才能从客户端发送到服务器。因此,在对等点之间交换数据之前,就会损失一个完整的往返时间 (round trip time) 。这种丢失的 RTT 是短网络对话延迟的重要组成部分。TCP Fast Open 就是为了解决这个问题。
tags:
- tcp
- network

categories:
- [tcp]
---

折腾 TCP 的同时看到了 TCP Fast Open 这个有趣的参数,它是对 TCP 的优化,无需等待 3 次握手,应用程序就可以通过 TCP 发送数据。

正常 TCP 建立连接过程:

```
Client Server
1. CLOSED LISTEN
2. SYN-SENT --> SYN M --> SYN-RECEIVED
3. ESTABLISHED <-- SYN N,ACK M+1 <-- SYN-RECEIVED
4. ESTABLISHED --> ACK N+1 --> ESTABLISHED
```

当前 TCP 实现的问题是,只有在连接发起方收到来自对等 TCP 的 ACK(确认)段后,才能在连接上交换数据。也就是说,只有在三次握手的第三步(发起方发送的ACK报文段)中,数据才能从客户端发送到服务器。因此,在对等点之间交换数据之前,就会损失一个完整的往返时间 (round trip time) 。这种丢失的 RTT 是短网络对话延迟的重要组成部分。TCP Fast Open 就是为了解决这个问题。

## 消除 RTT

```
Client Server
1. CLOSED LISTEN
2. SYN-SENT --> SYN, with cookie + data --> SYN-RECEIVED---
| Server TCP validates cookie, passes data to application
3. Client <-- SYN-ACK <-- SYN-RECEIVED---
4. Client <-- responses <-- Server Server can send responses before receiving client ACK
4. ESTABLISHED --> ACK --> ESTABLISHED
```

上图所示步骤如下:

1. 客户端 TCP 发送 SYN,其中包含 TFO cookie(指定为 TCP 选项)和来自客户端应用程序的数据。

2. 服务器 TCP 通过基于新 SYN 的源 IP 地址重复加密过程来验证 TFO cookie。如果 cookie 被证明是有效的,那么服务器 TCP 就可以确信这个 SYN 来自它声称来自的地址。这意味着服务器TCP可以立即将应用程序数据传递给服务器应用程序。

3. 从这里开始,TCP 会话正常进行:服务器 TCP 向客户端发送 SYN-ACK 段,然后客户端 TCP 进行确认,从而完成三向握手。服务器TCP还可以在收到客户端的 ACK **之前** 向客户端 TCP 发送响应数据段。

这是一个使用 `tcpdump` 查看使用了 `TCP_FAST_OPEN` 选项的抓包记录:

```
1. IP 127.0.0.1.51902 > 127.0.0.1.8000: Flags [S], seq 3550480872:3550480878, win 33280, options [mss 65495,sackOK,TS val 1437621030 ecr 0,nop,wscale 7,tfo cookie ce80700cf8e6113c,nop,nop], length 6
2. IP 127.0.0.1.8000 > 127.0.0.1.51902: Flags [S.], seq 2245778431, ack 3550480873, win 33280, options [mss 65495,sackOK,TS val 1437621030 ecr 1437621030,nop,wscale 7], length 0
3. IP 127.0.0.1.51902 > 127.0.0.1.8000: Flags [P.], seq 3550480873:3550480879, ack 2245778432, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 6
4. IP 127.0.0.1.8000 > 127.0.0.1.51902: Flags [.], ack 3550480879, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 0
5. IP 127.0.0.1.8000 > 127.0.0.1.51902: Flags [P.], seq 2245778432:2245778438, ack 3550480879, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 6
6. IP 127.0.0.1.51902 > 127.0.0.1.8000: Flags [.], ack 2245778438, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 0
7. IP 127.0.0.1.8000 > 127.0.0.1.51902: Flags [F.], seq 2245778438, ack 3550480879, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 0
8. IP 127.0.0.1.51902 > 127.0.0.1.8000: Flags [F.], seq 3550480879, ack 2245778439, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 0
9. IP 127.0.0.1.8000 > 127.0.0.1.51902: Flags [.], ack 3550480880, win 260, options [nop,nop,TS val 1437621030 ecr 1437621030], length 0
```

可以看到 `3` 这个数据段在 `4` 这个 ACK 数据包之前。并且 options 中有一个 `cookie` 字段。

Server.py:

```py
import socket


server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


server_address = ('localhost', 8880)
print('Starting up on {} port {}'.format(*server_address))
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_FASTOPEN, 1)
server_socket.bind(server_address)

server_socket.listen(1)

while True:
print('Waiting for a connection...')
connection, client_address = server_socket.accept()
try:
print('Connection from', client_address)

while True:
data = connection.recv(1024)
# TCP_QUICKACK
connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
print('Received:', data.decode())

if not data:
print('No data received from', client_address)
break

finally:
connection.close()
```

Client.py:

```py
import socket

addr = ("localhost", 8880)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.sendto("hello!".encode(), 536870912, addr)

print(s.recv(1000))
```

> `sendto` 需要提供 ip, 因为是 connectionless.
具体参考:

[1] https://lwn.net/Articles/508865/

0 comments on commit 6b88b93

Please sign in to comment.