tcpquiclab report initialized
This commit is contained in:
@@ -5,6 +5,7 @@ This guide adapts the Windows-based experiment manual for a Linux environment.
|
||||
## 1. Prerequisites
|
||||
|
||||
Ensure you have the following installed:
|
||||
|
||||
- `gcc` (Compiler)
|
||||
- `quiche` library (Headers and Shared Object installed)
|
||||
- `openssl` (For certificates)
|
||||
@@ -20,6 +21,7 @@ make
|
||||
```
|
||||
|
||||
This will generate:
|
||||
|
||||
- `tcp_server`, `tcp_client` (Task 1)
|
||||
- `quic_server`, `quic_client` (Task 2)
|
||||
- `tcp_perf_server`, `tcp_perf_client` (Task 3 Performance)
|
||||
@@ -30,23 +32,29 @@ This will generate:
|
||||
## 3. Task 1: Basic TCP Client-Server
|
||||
|
||||
1. **Start the Server:**
|
||||
|
||||
```bash
|
||||
./tcp_server
|
||||
```
|
||||
|
||||
2. **Run the Client (in a new terminal):**
|
||||
|
||||
```bash
|
||||
./tcp_client
|
||||
```
|
||||
|
||||
|
||||
**Expected Output:** The client sends "Hello...", server receives it and replies.
|
||||
|
||||
## 4. Task 2: Basic QUIC Client-Server
|
||||
|
||||
1. **Start the Server:**
|
||||
|
||||
```bash
|
||||
./quic_server
|
||||
```
|
||||
|
||||
2. **Run the Client (in a new terminal):**
|
||||
|
||||
```bash
|
||||
./quic_client
|
||||
```
|
||||
@@ -58,9 +66,11 @@ This will generate:
|
||||
### 3.1 Connection Establishment Time
|
||||
|
||||
1. Start capture on loopback:
|
||||
|
||||
```bash
|
||||
sudo tcpdump -i lo -w handshake.pcap
|
||||
```
|
||||
|
||||
*(Or use Wireshark on the `lo` interface)*
|
||||
|
||||
2. Run the TCP or QUIC client/server pairs again.
|
||||
@@ -82,11 +92,14 @@ We use Linux `tc` (Traffic Control) with `netem` instead of `clumsy`.
|
||||
**Scenario A: 5% Packet Loss**
|
||||
|
||||
1. Apply 5% loss to the loopback interface:
|
||||
|
||||
```bash
|
||||
sudo tc qdisc add dev lo root netem loss 5%
|
||||
```
|
||||
|
||||
2. Run the perf tests again.
|
||||
3. **Important:** Remove the rule after testing!
|
||||
|
||||
```bash
|
||||
sudo tc qdisc del dev lo root
|
||||
```
|
||||
@@ -94,11 +107,14 @@ We use Linux `tc` (Traffic Control) with `netem` instead of `clumsy`.
|
||||
**Scenario B: 100ms Delay**
|
||||
|
||||
1. Apply 100ms delay:
|
||||
|
||||
```bash
|
||||
sudo tc qdisc add dev lo root netem delay 100ms
|
||||
```
|
||||
|
||||
2. Run the perf tests again.
|
||||
3. Remove the rule:
|
||||
|
||||
```bash
|
||||
sudo tc qdisc del dev lo root
|
||||
```
|
||||
@@ -111,29 +127,38 @@ This task compares the performance of 5 parallel TCP connections against a singl
|
||||
Establish 5 TCP connections simultaneously, each transferring 20MB (Total 100MB).
|
||||
|
||||
1. Start TCP Multi-Connection Server:
|
||||
|
||||
```bash
|
||||
./tcp_multi_server
|
||||
```
|
||||
|
||||
2. Run TCP Multi-Connection Client:
|
||||
|
||||
```bash
|
||||
./tcp_multi_client
|
||||
```
|
||||
|
||||
3. Record total time and throughput from the server output.
|
||||
|
||||
**Scenario 2: QUIC Single-Connection Multi-Streaming**
|
||||
Establish 1 QUIC connection and open 5 streams concurrently, each transferring 20MB (Total 100MB).
|
||||
|
||||
1. Start QUIC Multi-Stream Server:
|
||||
|
||||
```bash
|
||||
./quic_multi_server
|
||||
```
|
||||
|
||||
2. Run QUIC Multi-Stream Client:
|
||||
|
||||
```bash
|
||||
./quic_multi_client
|
||||
```
|
||||
|
||||
3. Record the performance statistics.
|
||||
|
||||
**Analysis Points:**
|
||||
|
||||
- Compare completion times in a normal network.
|
||||
- Use `tc` to simulate packet loss (e.g., 5%). Observe how QUIC's multiplexing avoids TCP's Head-of-Line (HoL) blocking, where a single lost packet in one TCP connection doesn't stall the other streams in QUIC.
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
## 1. 预备工作
|
||||
|
||||
确保已安装以下工具和库:
|
||||
|
||||
- `gcc` (编译器)
|
||||
- `quiche` 库 (已安装头文件和共享库/静态库)
|
||||
- `openssl` (用于生成证书)
|
||||
@@ -20,6 +21,7 @@ make
|
||||
```
|
||||
|
||||
这将生成以下可执行文件:
|
||||
|
||||
- `tcp_server`, `tcp_client` (任务一:基础 TCP)
|
||||
- `quic_server`, `quic_client` (任务二:基础 QUIC)
|
||||
- `tcp_perf_server`, `tcp_perf_client` (任务三:TCP 性能测试)
|
||||
@@ -30,23 +32,29 @@ make
|
||||
## 3. 任务一:基础 TCP 客户端-服务器
|
||||
|
||||
1. **启动服务器:**
|
||||
|
||||
```bash
|
||||
./tcp_server
|
||||
```
|
||||
|
||||
2. **运行客户端 (在新的终端窗口):**
|
||||
|
||||
```bash
|
||||
./tcp_client
|
||||
```
|
||||
|
||||
|
||||
**预期输出:** 客户端发送消息,服务器接收并回复。
|
||||
|
||||
## 4. 任务二:基础 QUIC 客户端-服务器
|
||||
|
||||
1. **启动服务器:**
|
||||
|
||||
```bash
|
||||
./quic_server
|
||||
```
|
||||
|
||||
2. **运行客户端 (在新的终端窗口):**
|
||||
|
||||
```bash
|
||||
./quic_client
|
||||
```
|
||||
@@ -58,9 +66,11 @@ make
|
||||
### 3.1 连接建立时间
|
||||
|
||||
1. 在回环接口 (`lo`) 上开始抓包:
|
||||
|
||||
```bash
|
||||
sudo tcpdump -i lo -w handshake.pcap
|
||||
```
|
||||
|
||||
*(或者使用 Wireshark 监听 `lo` 接口)*
|
||||
|
||||
2. 再次运行 TCP 或 QUIC 的客户端/服务器程序。
|
||||
@@ -82,11 +92,14 @@ make
|
||||
**场景 A: 5% 丢包率**
|
||||
|
||||
1. 设置回环接口 5% 丢包:
|
||||
|
||||
```bash
|
||||
sudo tc qdisc add dev lo root netem loss 5%
|
||||
```
|
||||
|
||||
2. 再次运行性能测试程序。
|
||||
3. **重要:** 测试结束后删除规则!
|
||||
|
||||
```bash
|
||||
sudo tc qdisc del dev lo root
|
||||
```
|
||||
@@ -94,11 +107,14 @@ make
|
||||
**场景 B: 100ms 延迟**
|
||||
|
||||
1. 设置 100ms 延迟:
|
||||
|
||||
```bash
|
||||
sudo tc qdisc add dev lo root netem delay 100ms
|
||||
```
|
||||
|
||||
2. 再次运行性能测试程序。
|
||||
3. 删除规则:
|
||||
|
||||
```bash
|
||||
sudo tc qdisc del dev lo root
|
||||
```
|
||||
@@ -111,29 +127,38 @@ make
|
||||
同时建立 5 个 TCP 连接,每个连接传输 20MB 数据 (总计 100MB)。
|
||||
|
||||
1. 启动 TCP 多连接服务器:
|
||||
|
||||
```bash
|
||||
./tcp_multi_server
|
||||
```
|
||||
|
||||
2. 启动 TCP 多连接客户端:
|
||||
|
||||
```bash
|
||||
./tcp_multi_client
|
||||
```
|
||||
|
||||
3. 记录服务器输出的总时间与吞吐量。
|
||||
|
||||
**场景 2: QUIC 单连接多流复用**
|
||||
建立 1 个 QUIC 连接,在其中同时开启 5 个流 (Stream),每个流传输 20MB 数据 (总计 100MB)。
|
||||
|
||||
1. 启动 QUIC 多流服务器:
|
||||
|
||||
```bash
|
||||
./quic_multi_server
|
||||
```
|
||||
|
||||
2. 启动 QUIC 多流客户端:
|
||||
|
||||
```bash
|
||||
./quic_multi_client
|
||||
```
|
||||
|
||||
3. 记录服务器输出的统计数据。
|
||||
|
||||
**分析重点:**
|
||||
|
||||
- 在正常网络下,两者的总耗时差异。
|
||||
- 使用 `tc` 模拟丢包 (如 5%) 后,对比两者性能下降的幅度。QUIC 的多流复用应能避免 TCP 的“队头阻塞”问题 (即一个包丢失不影响其他流的传输),从而在丢包环境下表现更优。
|
||||
|
||||
|
||||
154
network/tcpquiclab/labtemplate.typ
Executable file
154
network/tcpquiclab/labtemplate.typ
Executable file
@@ -0,0 +1,154 @@
|
||||
#let times = "Times LT Pro"
|
||||
#let times = "Times New Roman"
|
||||
#let song = (times, "Noto Serif CJK SC")
|
||||
#let hei = (times, "Noto Sans CJK SC")
|
||||
#let kai = (times, "Noto Serif CJK SC")
|
||||
#let xbsong = (times, "Noto Serif CJK SC")
|
||||
#let fsong = (times, "Noto Serif CJK SC")
|
||||
#let code = (times, "JetBrains Mono")
|
||||
#let nudtlabpaper(title: "",
|
||||
author: "",
|
||||
id: "",
|
||||
training_type:"",
|
||||
grade: "",
|
||||
major: "",
|
||||
department: "",
|
||||
advisor: "",
|
||||
jobtitle: "",
|
||||
lab: "",
|
||||
date: "",
|
||||
header_str: "",
|
||||
body) = {
|
||||
// Set the document's basic properties.
|
||||
set document(author: author, title: title)
|
||||
set page(
|
||||
|
||||
margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm),
|
||||
)
|
||||
|
||||
// Title row.
|
||||
v(158pt)
|
||||
align(center)[
|
||||
#block(text(weight: 700, size: 30pt, font: hei, tracking: 15pt, "计算机网络"))
|
||||
]
|
||||
align(center)[
|
||||
#block(text(weight: 700, size: 30pt, font: song, tracking: 15pt, "本科实验报告"))
|
||||
]
|
||||
|
||||
v(103pt)
|
||||
pad(
|
||||
left: 1em,
|
||||
right: 1em,
|
||||
grid(
|
||||
columns: (80pt, 1fr),
|
||||
rows: (17pt, auto),
|
||||
text(weight: 700, size: 16pt, font: song, "实验名称:"),
|
||||
align(center, text(weight: "regular", size: 16pt, font: song, title)),
|
||||
text(""),
|
||||
line(length: 100%)
|
||||
)
|
||||
// #block(text(weight: 700, 1.75em, title))
|
||||
// underline(text(weight: 700, size: 16pt, font: song, title))
|
||||
)
|
||||
|
||||
// Author information.
|
||||
|
||||
v(82.5pt)
|
||||
|
||||
grid(
|
||||
columns: (0.25fr, 0.25fr, 0.25fr, 0.25fr),
|
||||
rows: (15pt, 8pt, 15pt, 8pt, 15pt, 8pt, 15pt, 8pt, 15pt),
|
||||
text(size: 14pt, font: song, tracking: 10pt, "学员姓名"),
|
||||
align(center, text(size: 14pt, font: song, author)),
|
||||
text(size: 14pt, font: song, tracking: 54pt, "学号"),
|
||||
align(center, text(size: 14pt, font: times, id)),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(size: 14pt, font: song, tracking: 9pt, "培养类型"),
|
||||
align(center, text(size: 14pt, font: song, training_type)),
|
||||
text(size: 14pt, font: song, tracking: 54pt, "年级"),
|
||||
align(center, text(size: 14pt, font: times, grade)),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(size: 14pt, font: song, tracking: 54pt, "专业"),
|
||||
align(center, text(size: 14pt, font: song, major)),
|
||||
text(size: 14pt, font: song, tracking: 9pt, "所属学院"),
|
||||
align(center, text(size: 14pt, font: song, department)),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(size: 14pt, font: song, tracking: 9pt, "指导教员"),
|
||||
align(center, text(size: 14pt, font: song, advisor)),
|
||||
text(size: 14pt, font: song, tracking: 54pt, "职称"),
|
||||
align(center, text(size: 14pt, font: song, jobtitle)),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(size: 14pt, font: song, tracking: 20pt, "实验室"),
|
||||
align(center, text(size: 14pt, font: song, lab)),
|
||||
text(size: 14pt, font: song, tracking: 9pt, "实验时间"),
|
||||
align(center, text(size: 14pt, font: song, date)),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
text(""),
|
||||
line(length: 100%),
|
||||
)
|
||||
|
||||
v(50.5pt)
|
||||
align(center, text(font: hei, size: 15pt, "国防科技大学教育训练部制"))
|
||||
|
||||
pagebreak()
|
||||
|
||||
set page(
|
||||
margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm),
|
||||
numbering: "i",
|
||||
number-align: center,
|
||||
)
|
||||
|
||||
v(14pt)
|
||||
align(center)[
|
||||
#block(text(font: hei, size: 14pt, "《本科实验报告》填写说明"))
|
||||
]
|
||||
|
||||
v(14pt)
|
||||
text("")
|
||||
par(first-line-indent: 2em, text(font: song, size: 12pt, "实验报告内容编排应符合以下要求:"))
|
||||
|
||||
par(first-line-indent: 2em, text(font: fsong, size: 12pt, "(1)采用A4(21cm×29.7cm)白色复印纸,单面黑字。上下左右各侧的页边距均为3cm;缺省文档网格:字号为小4号,中文为宋体,英文和阿拉伯数字为Times New Roman,每页30行,每行36字;页脚距边界为2.5cm,页码置于页脚、居中,采用小5号阿拉伯数字从1开始连续编排,封面不编页码。"))
|
||||
|
||||
par(first-line-indent: 2em, text(font: fsong, size: 12pt, "(2)报告正文最多可设四级标题,字体均为黑体,第一级标题字号为4号,其余各级标题为小4号;标题序号第一级用“一、”、“二、”……,第二级用“(一)”、“(二)” ……,第三级用“1.”、“2.” ……,第四级用“(1)”、“(2)” ……,分别按序连续编排。"))
|
||||
|
||||
par(first-line-indent: 2em, text(font: fsong, size: 12pt, "(3)正文插图、表格中的文字字号均为5号。"))
|
||||
|
||||
pagebreak()
|
||||
|
||||
set page(
|
||||
margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm),
|
||||
numbering: "1",
|
||||
number-align: center,
|
||||
)
|
||||
|
||||
set heading(numbering: "1.1")
|
||||
// set text(font: hei, lang: "zh")
|
||||
|
||||
show heading: it => box(width: 100%)[
|
||||
#v(0.50em)
|
||||
#set text(font: hei)
|
||||
#counter(heading).display()
|
||||
// #h(0.5em)
|
||||
#it.body
|
||||
]
|
||||
// Main body.
|
||||
set par(justify: true)
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
#let para(t) = par(first-line-indent: 2em, text(font: song, size: 10.5pt, t))
|
||||
|
||||
22341
network/tcpquiclab/main.pdf
Normal file
22341
network/tcpquiclab/main.pdf
Normal file
File diff suppressed because it is too large
Load Diff
970
network/tcpquiclab/main.typ
Executable file
970
network/tcpquiclab/main.typ
Executable file
@@ -0,0 +1,970 @@
|
||||
#import "labtemplate.typ": *
|
||||
#show: nudtlabpaper.with(title: "TCP 与 QUIC 协议性能对比分析实验",
|
||||
author: "程景愉",
|
||||
id: "202302723005",
|
||||
training_type: "无军籍",
|
||||
grade: "2023",
|
||||
major: "网络工程",
|
||||
department: "计算机学院",
|
||||
advisor: "逄德明",
|
||||
jobtitle: "教授",
|
||||
lab: "307-211",
|
||||
date: "2026.1.12",
|
||||
header_str: "《计算机网络》实验报告",
|
||||
)
|
||||
|
||||
#set page(header: [
|
||||
#set par(spacing: 6pt)
|
||||
#align(center)[#text(size: 11pt)[《计算机网络》实验报告]]
|
||||
#v(-0.3em)
|
||||
#line(length: 100%, stroke: (thickness: 1pt))
|
||||
],)
|
||||
|
||||
#show heading: it => box(width: 100%)[
|
||||
#v(0.50em)
|
||||
#set text(font: hei)
|
||||
#it.body
|
||||
]
|
||||
|
||||
#outline(title: "目录",depth: 3, indent: 1em)
|
||||
// #pagebreak()
|
||||
// #outline(
|
||||
// title: [图目录],
|
||||
// target: figure.where(kind: image),
|
||||
// )
|
||||
|
||||
#show heading: it => box(width: 100%)[
|
||||
#v(0.50em)
|
||||
#set text(font: hei)
|
||||
#counter(heading).display()
|
||||
// #h(0.5em)
|
||||
#it.body
|
||||
]
|
||||
#set enum(indent: 0.5em,body-indent: 0.5em,)
|
||||
#pagebreak()
|
||||
|
||||
|
||||
= 实验概要
|
||||
== 实验内容
|
||||
本次实验的主要内容是对比分析 TCP 与 QUIC 两种传输协议的性能差异。实验包含基础任务和性能测试任务两部分,具体任务要求如下:
|
||||
|
||||
- 基础任务:基于 TCP 和 QUIC 协议分别实现客户端-服务器通信程序。TCP 程序使用标准 socket 编程,实现基本的连接建立、数据发送和接收功能;QUIC 程序使用 quiche 库,实现基于 UDP 的可靠传输功能。两个程序都需要完成监听指定端口、接受客户端连接、接收消息并返回响应的基本功能。
|
||||
|
||||
- 性能测试任务:在基础任务实现的基础上,完成以下性能对比测试:
|
||||
+ 连接建立时间对比:测量 TCP 三次握手和 QUIC 0-RTT 连接建立的时间差异
|
||||
+ 吞吐量测试:在不同网络条件下(正常网络、5%丢包、100ms延迟)对比两种协议的传输性能
|
||||
+ 多路复用性能测试:对比 5 个 TCP 连接与单个 QUIC 连接上 5 个流的传输性能,分析队头阻塞问题
|
||||
+ 网络异常恢复测试:模拟网络中断后恢复的场景,对比两种协议的恢复能力和连接迁移能力
|
||||
|
||||
== 实验要求
|
||||
本实验的具体过程及对应要求如下:
|
||||
- 实验开始前准备工作:在实验开始前,学员需要掌握 C 语言编程基础,理解 TCP/IP 协议栈的工作原理,特别是 TCP 协议的三次握手、拥塞控制、流量控制机制,以及 QUIC 协议基于 UDP 的传输机制、多路复用和 0-RTT 连接特性。同时,熟悉 socket 编程和 quiche 库的使用方法,了解网络性能测试的基本方法。
|
||||
- 实验过程中:按照实验要求,完成 TCP 和 QUIC 客户端-服务器程序的实现。具体步骤包括:TCP 程序使用 socket()、bind()、listen()、accept() 等 API 实现服务器端,使用 socket()、connect()、send()、recv() 等 API 实现客户端;QUIC 程序使用 quiche 库的配置、连接建立、流管理和数据传输接口实现服务器和客户端。然后在不同网络条件下进行性能测试,使用 tc 或 clumsy 工具模拟丢包和延迟环境,记录测试数据。
|
||||
- 实验结束后:总结 TCP 和 QUIC 协议的性能差异,详细描述两种协议在各种网络条件下的表现,分析 QUIC 协议的优势和不足,并根据实验要求撰写实验报告,展示实验结果和数据分析。
|
||||
|
||||
== 实验目的
|
||||
在现代网络环境中,TCP 协议作为互联网的基础传输协议,广泛应用于各种应用场景。然而,随着网络技术的发展和新型应用的出现,TCP 协议的一些局限性逐渐显现,如队头阻塞、连接建立延迟、协议更新困难等问题。QUIC 协议作为新一代传输协议,旨在解决这些问题,提供更快、更可靠、更安全的传输服务。
|
||||
|
||||
通过本次实验,学员将深入理解 TCP 和 QUIC 两种传输协议的工作原理和性能特点,掌握网络编程的基本方法,学习如何使用专业的网络分析工具进行性能测试。具体目的包括:
|
||||
|
||||
1. *理解协议原理*:深入理解 TCP 协议的三次握手、拥塞控制、流量控制机制,以及 QUIC 协议基于 UDP 的传输机制、多路复用、0-RTT 连接和连接迁移等特性。
|
||||
|
||||
2. *掌握编程技术*:掌握 Linux/Unix 环境下的 socket 编程技术,学习使用 quiche 库实现 QUIC 协议,理解网络编程中的异步 I/O、事件驱动等高级技术。
|
||||
|
||||
3. *性能分析能力*:学习使用 Wireshark、tc、clumsy 等工具进行网络性能测试和分析,掌握吞吐量、延迟、丢包率等关键性能指标的测量方法。
|
||||
|
||||
4. *协议对比分析*:通过实际测试,对比分析 TCP 和 QUIC 在不同网络条件下的性能差异,理解 QUIC 协议的优势和适用场景。
|
||||
|
||||
5. *实践能力提升*:通过亲手实现两种协议的客户端-服务器程序,培养实际编程和问题解决的能力,为后续学习更复杂的网络协议和系统奠定基础。
|
||||
|
||||
本次实验不仅是对网络协议理论的验证,更是对现代网络编程技术的实践,对于理解互联网传输层协议的发展趋势具有重要意义。
|
||||
|
||||
// that retains the correct baseline.
|
||||
#show raw.where(block: false): it => box(
|
||||
text(font: "Consolas", it),
|
||||
fill: luma(240),
|
||||
inset: (x: 3pt, y: 0pt),
|
||||
outset: (y: 3pt),
|
||||
radius: 2pt,
|
||||
)
|
||||
|
||||
// Display block code in a larger block
|
||||
// with more padding.
|
||||
#show raw.where(block: true): it => block(
|
||||
text(font: "Consolas", it),
|
||||
fill: luma(240),
|
||||
inset: 10pt,
|
||||
radius: 4pt,
|
||||
width: 100%,
|
||||
)
|
||||
|
||||
= 实验原理及方案
|
||||
#para[
|
||||
本次实验通过实现 TCP 和 QUIC 两种传输协议的客户端-服务器程序,对比分析它们在不同网络条件下的性能表现。TCP(Transmission Control Protocol)是互联网的核心传输协议,提供可靠的、面向连接的字节流传输服务;QUIC(Quick UDP Internet Connections)是 Google 提出的基于 UDP 的新一代传输协议,旨在解决 TCP 的队头阻塞、连接建立延迟等问题。
|
||||
]
|
||||
== TCP 协议原理
|
||||
TCP 协议是传输层的核心协议,提供可靠的、面向连接的、基于字节流的传输服务。TCP 协议的主要特性包括:
|
||||
|
||||
*三次握手*:TCP 连接建立需要三次握手过程。客户端发送 SYN 包,服务器回复 SYN-ACK 包,客户端再回复 ACK 包。这个过程确保双方都准备好接收数据,但引入了至少 1-RTT 的连接建立延迟。在高延迟网络中,三次握手会对性能产生显著影响。
|
||||
|
||||
*可靠传输*:TCP 通过序列号、确认应答和重传机制实现可靠传输。每个数据包都有序列号,接收方收到数据后发送 ACK 确认。如果发送方在超时时间内未收到 ACK,则重传数据。这种机制确保了数据的完整性,但也增加了协议的复杂性和延迟。
|
||||
|
||||
*流量控制*:TCP 使用滑动窗口机制进行流量控制。接收方通过通告窗口大小告诉发送方当前可接收的数据量,避免发送方发送过快导致接收方缓冲区溢出。窗口大小根据网络状况动态调整,实现高效的流量控制。
|
||||
|
||||
*拥塞控制*:TCP 通过拥塞窗口控制发送速率,避免网络拥塞。常见的拥塞控制算法包括 Reno、Cubic、BBR 等。当检测到丢包时,TCP 会降低发送速率;当网络状况良好时,会逐步增加发送速率。这种机制保证了网络的稳定性,但也限制了在高延迟或高丢包环境下的性能。
|
||||
|
||||
*队头阻塞*:TCP 是基于字节流的协议,数据按顺序传输。如果某个数据包丢失,后续数据包必须等待该包重传成功后才能交付给应用层,这种现象称为队头阻塞。在多路复用场景下,队头阻塞会严重影响性能。
|
||||
|
||||
本次实验中,TCP 程序使用标准的 socket API 实现。服务器端通过 `socket()`、`bind()`、`listen()`、`accept()` 等函数建立监听套接字,接受客户端连接;客户端通过 `socket()`、`connect()` 建立连接,使用 `send()`、`recv()` 进行数据传输。程序使用阻塞式 I/O 模型,简化了实现逻辑。
|
||||
== QUIC 协议原理
|
||||
QUIC 协议是基于 UDP 的传输层协议,旨在解决 TCP 的局限性。QUIC 的主要特性包括:
|
||||
|
||||
*0-RTT 连接建立*:QUIC 支持在连接建立时发送应用数据。如果客户端之前与服务器建立过连接,可以缓存服务器的配置信息,在重新连接时直接发送数据,实现 0-RTT 的连接建立延迟。这相比 TCP 的三次握手显著降低了连接建立时间。
|
||||
|
||||
*多路复用*:QUIC 在单个连接上支持多个独立的流(Stream)。每个流可以独立传输数据,一个流的丢包不会影响其他流的传输,从而解决了 TCP 的队头阻塞问题。这对于 HTTP/2 等多路复用协议尤其重要。
|
||||
|
||||
*连接迁移*:QUIC 使用连接 ID 而不是四元组(源 IP、源端口、目的 IP、目的端口)标识连接,因此客户端的 IP 地址或端口变化不会导致连接中断。这支持移动设备在网络切换时保持连接,提高了移动网络的用户体验。
|
||||
|
||||
*内置加密*:QUIC 协议内置了 TLS 1.3 加密,所有数据包都经过加密传输,提高了安全性。与 TCP + TLS 相比,QUIC 减少了握手轮次,降低了连接建立延迟。
|
||||
|
||||
*可插拔的拥塞控制*:QUIC 支持多种拥塞控制算法,并且可以在运行时切换。本次实验使用 Reno 算法,与 TCP 的实现保持一致,便于公平对比。
|
||||
|
||||
本次实验中,QUIC 程序使用 Cloudflare 的 quiche 库实现。服务器端创建 UDP socket,配置 QUIC 参数(证书、密钥、应用协议、流限制等),监听端口并接受连接;客户端创建 QUIC 连接,建立后通过流发送数据。程序使用非阻塞 I/O 模型,通过轮询机制处理网络事件,确保及时响应。
|
||||
== 性能测试方案
|
||||
本次实验设计了多个性能测试场景,从不同角度对比 TCP 和 QUIC 的性能差异。
|
||||
|
||||
*连接建立时间测试*:使用 Wireshark 捕获 TCP 和 QUIC 的连接建立过程,记录从客户端发送第一个包到完成握手的时间。TCP 测量从 SYN 到 ACK 的时间,QUIC 测量从 ClientHello 到握手完成的时间。重复测试 3 次,计算平均值。
|
||||
|
||||
*吞吐量测试*:修改程序实现大文件传输功能(100MB 随机数据),在不同网络条件下测试吞吐量:
|
||||
- 正常网络:无丢包、无延迟
|
||||
- 丢包网络:使用 `tc qdisc add dev eth0 root netem loss 5%` 模拟 5% 丢包率
|
||||
- 延迟网络:使用 `tc qdisc add dev eth0 root netem delay 100ms` 模拟 100ms 延迟
|
||||
|
||||
计算并对比两种协议的吞吐量(MB/s),分析丢包和延迟对性能的影响。
|
||||
|
||||
*多路复用性能测试*:设计多流传输测试,同时建立 5 个 TCP 连接传输数据(每个连接传输 20MB),在单个 QUIC 连接上建立 5 个流传输数据(每个流传输 20MB)。测量并对比两种方式的总传输时间,分析 QUIC 多路复用如何解决 TCP 的队头阻塞问题。
|
||||
|
||||
*网络异常恢复测试*:模拟网络中断后恢复的场景:
|
||||
1. 建立连接并开始传输数据
|
||||
2. 使用 `tc qdisc add dev eth0 root netem loss 100%` 模拟网络中断
|
||||
3. 30 秒后使用 `tc qdisc del dev eth0 root` 恢复网络
|
||||
4. 对比两种协议的恢复能力和数据完整性
|
||||
|
||||
测试 QUIC 的连接迁移能力,在传输过程中改变客户端的 IP 地址或端口,观察连接是否保持正常。
|
||||
|
||||
*测试环境*:实验使用 Tailscale 虚拟局域网,两台主机通过 Tailscale 连接,模拟真实的网络环境。一台主机运行服务器程序,另一台主机运行客户端程序,传输 100MB 数据,记录传输时间和吞吐量。
|
||||
= 实验环境
|
||||
== 实验设备与软件
|
||||
#align(center)[#table(
|
||||
columns: (auto, auto),
|
||||
rows:(auto,auto,auto,auto,auto),
|
||||
inset: 10pt,
|
||||
align: horizon+center,
|
||||
table.header(
|
||||
[*名称*], [*型号或版本*],
|
||||
),
|
||||
"操作系统", "Linux 6.18.6-2-cachyos",
|
||||
"Tailscale", "Tailscale 虚拟局域网",
|
||||
"编译器", "GCC",
|
||||
"构建工具", "Make",
|
||||
"Wireshark", "Wireshark 4.6.3"
|
||||
)]
|
||||
=== 软件环境
|
||||
本实验的软件开发环境包括以下工具和库:
|
||||
|
||||
- *操作系统*:Linux 6.18.6-2-cachyos,提供稳定的开发和运行环境。两台主机通过 Tailscale 建立虚拟局域网连接,模拟真实的网络环境。
|
||||
|
||||
- *编译器*:GCC,支持 C99 标准,用于编译 TCP 和 QUIC 程序。
|
||||
|
||||
- *构建工具*:Make,用于管理编译过程,简化编译命令。
|
||||
|
||||
- *网络库*:
|
||||
- TCP 程序使用标准 POSIX socket API(`<sys/socket.h>`、`<arpa/inet.h>` 等)
|
||||
- QUIC 程序使用 Cloudflare 的 quiche 库(`<quiche.h>`),提供 QUIC 协议的 C 语言接口
|
||||
|
||||
- *网络模拟工具*:
|
||||
- `tc`(Traffic Control):Linux 内核流量控制工具,用于模拟丢包、延迟等网络条件
|
||||
- `clumsy`:Windows 平台的网络故障模拟工具,功能与 `tc` 类似
|
||||
|
||||
- *抓包工具*:Wireshark 4.6.3,网络协议分析工具,用于捕获和分析网络数据包,验证协议实现的正确性,测量连接建立时间。
|
||||
|
||||
- *证书管理*:使用 OpenSSL 生成 QUIC 协议所需的 TLS 证书和私钥(`cert.crt`、`cert.key`)。
|
||||
|
||||
- *文本编辑器*:支持语法高亮的代码编辑器,用于编写和调试代码。
|
||||
|
||||
开发环境配置简单,只需安装 GCC、Make 和 quiche 库即可开始开发。quiche 库通过 Rust 编译生成 C 语言接口,需要在系统中安装 Rust 和 Cargo。本实验在 Linux 环境下完成测试,使用 Tailscale 建立虚拟局域网,两台主机的 IP 地址分别为 `100.115.45.1`(服务器)和 `100.115.45.2`(客户端)。
|
||||
= 实验步骤
|
||||
== 环境配置
|
||||
=== Tailscale 虚拟局域网配置
|
||||
实验使用 Tailscale 建立虚拟局域网连接两台主机。Tailscale 是一种基于 WireGuard 的 VPN 服务,能够穿透 NAT,建立安全的点对点连接。配置步骤如下:
|
||||
|
||||
1. 在两台主机上安装 Tailscale 客户端
|
||||
2. 使用 `sudo tailscale up` 命令登录 Tailscale 账号
|
||||
3. 使用 `tailscale ip -4` 命令查看分配的 IP 地址
|
||||
4. 配置服务器主机 IP 为 `100.115.45.1`,客户端主机 IP 为 `100.115.45.2`
|
||||
|
||||
Tailscale 提供了稳定的网络连接,支持 UDP 和 TCP 协议,非常适合本实验的网络测试需求。
|
||||
|
||||
=== 证书生成
|
||||
QUIC 协议需要 TLS 证书进行加密传输。使用 OpenSSL 生成自签名证书:
|
||||
|
||||
```bash
|
||||
openssl req -x509 -newkey rsa:4096 -keyout cert.key -out cert.crt -days 365 -nodes
|
||||
```
|
||||
|
||||
生成的 `cert.crt` 和 `cert.key` 文件用于 QUIC 服务器和客户端的 TLS 握手。
|
||||
|
||||
=== 网络模拟配置
|
||||
使用 `tc` 命令模拟丢包和延迟环境:
|
||||
|
||||
```bash
|
||||
# 模拟 5% 丢包率
|
||||
sudo tc qdisc add dev tailscale0 root netem loss 5%
|
||||
|
||||
# 模拟 100ms 延迟
|
||||
sudo tc qdisc add dev tailscale0 root netem delay 100ms
|
||||
|
||||
# 恢复正常网络
|
||||
sudo tc qdisc del dev tailscale0 root
|
||||
```
|
||||
|
||||
注意:`tailscale0` 是 Tailscale 的网络接口名称,实际使用时需要根据系统配置调整。
|
||||
== 实现 TCP 客户端-服务器程序
|
||||
=== TCP 服务器实现
|
||||
TCP 服务器使用标准 socket API 实现,主要步骤如下:
|
||||
|
||||
*创建套接字*:使用 `socket(AF_INET, SOCK_STREAM, 0)` 创建 TCP 套接字。
|
||||
|
||||
```c
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
perror("socket failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
*绑定端口*:使用 `bind()` 将套接字绑定到指定端口(8080),设置 `SO_REUSEADDR` 选项允许快速重启。
|
||||
|
||||
```c
|
||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
|
||||
perror("setsockopt");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(PORT);
|
||||
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
perror("bind failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
*监听连接*:使用 `listen()` 开始监听客户端连接,队列长度设置为 3。
|
||||
|
||||
```c
|
||||
if (listen(server_fd, 3) < 0) {
|
||||
perror("listen");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
*接受连接*:使用 `accept()` 接受客户端连接,返回新的套接字用于通信。
|
||||
|
||||
```c
|
||||
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
|
||||
perror("accept");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
*接收数据*:使用 `read()` 接收客户端发送的数据,打印接收到的字节数和内容。
|
||||
|
||||
```c
|
||||
int valread = read(new_socket, buffer, BUFFER_SIZE);
|
||||
if (valread > 0) {
|
||||
printf("Received %d bytes: %s\n", valread, buffer);
|
||||
}
|
||||
```
|
||||
|
||||
*发送响应*:使用 `send()` 向客户端发送响应,包含接收到的数据长度。
|
||||
|
||||
```c
|
||||
char response[BUFFER_SIZE];
|
||||
snprintf(response, BUFFER_SIZE, "Server received %d bytes", valread);
|
||||
send(new_socket, response, strlen(response), 0);
|
||||
```
|
||||
|
||||
*关闭套接字*:通信完成后,关闭客户端套接字和服务器套接字,释放资源。
|
||||
|
||||
```c
|
||||
close(new_socket);
|
||||
close(server_fd);
|
||||
```
|
||||
|
||||
=== TCP 客户端实现
|
||||
TCP 客户端的主要步骤如下:
|
||||
|
||||
*创建套接字*:使用 `socket(AF_INET, SOCK_STREAM, 0)` 创建 TCP 套接字。
|
||||
|
||||
```c
|
||||
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
printf("\n Socket creation error \n");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
*配置服务器地址*:设置服务器的 IP 地址和端口号。
|
||||
|
||||
```c
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_port = htons(PORT);
|
||||
|
||||
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
|
||||
printf("\nInvalid address/ Address not supported \n");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
*建立连接*:使用 `connect()` 连接到服务器,触发 TCP 三次握手。
|
||||
|
||||
```c
|
||||
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||
printf("\nConnection Failed \n");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
*发送数据*:使用 `send()` 向服务器发送消息。
|
||||
|
||||
```c
|
||||
send(sock, hello, strlen(hello), 0);
|
||||
printf("Message sent to server: %s\n", hello);
|
||||
```
|
||||
|
||||
*接收响应*:使用 `read()` 接收服务器的响应。
|
||||
|
||||
```c
|
||||
int valread = read(sock, buffer, BUFFER_SIZE);
|
||||
if (valread > 0) {
|
||||
printf("Server response: %s\n", buffer);
|
||||
}
|
||||
```
|
||||
|
||||
*关闭套接字*:通信完成后,关闭套接字。
|
||||
|
||||
```c
|
||||
close(sock);
|
||||
```
|
||||
|
||||
=== 编译与运行
|
||||
使用 Make 编译 TCP 程序:
|
||||
|
||||
```bash
|
||||
make tcp_server tcp_client
|
||||
```
|
||||
|
||||
运行服务器和客户端:
|
||||
|
||||
```bash
|
||||
# 服务器端
|
||||
./tcp_server
|
||||
|
||||
# 客户端
|
||||
./tcp_client
|
||||
```
|
||||
|
||||
服务器输出:
|
||||
```
|
||||
TCP Server listening on port 8080...
|
||||
Client connected.
|
||||
Received 22 bytes: Hello from TCP Client
|
||||
Response sent to client.
|
||||
```
|
||||
|
||||
客户端输出:
|
||||
```
|
||||
Message sent to server: Hello from TCP Client
|
||||
Server response: Server received 22 bytes
|
||||
```
|
||||
== 实现 QUIC 客户端-服务器程序
|
||||
=== QUIC 服务器实现
|
||||
QUIC 服务器使用 quiche 库实现,主要步骤如下:
|
||||
|
||||
*创建 QUIC 配置*:初始化 quiche 配置对象,设置证书、密钥、应用协议、流限制等参数。
|
||||
|
||||
```c
|
||||
quiche_config *config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
|
||||
if (config == NULL) {
|
||||
fprintf(stderr, "failed to create config\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (quiche_config_load_cert_chain_from_pem_file(config, "cert.crt") < 0) {
|
||||
fprintf(stderr, "failed to load certificate chain\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (quiche_config_load_priv_key_from_pem_file(config, "cert.key") < 0) {
|
||||
fprintf(stderr, "failed to load private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
quiche_config_set_application_protos(config, (uint8_t *) "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38);
|
||||
quiche_config_set_max_idle_timeout(config, 5000);
|
||||
quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
|
||||
quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
|
||||
quiche_config_set_initial_max_data(config, 10000000);
|
||||
quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
|
||||
quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
|
||||
quiche_config_set_initial_max_streams_bidi(config, 100);
|
||||
quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
|
||||
```
|
||||
|
||||
*创建 UDP 套接字*:使用 `socket(AF_INET, SOCK_DGRAM, 0)` 创建 UDP 套接字,绑定到指定端口(8888)。
|
||||
|
||||
```c
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(8888);
|
||||
sa.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
perror("bind");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
*设置非阻塞模式*:使用 `fcntl()` 设置套接字为非阻塞模式,避免主循环阻塞。
|
||||
|
||||
```c
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
```
|
||||
|
||||
*主循环处理*:主循环不断接收 UDP 数据包,解析 QUIC 头部,创建或更新连接对象,处理流数据,发送响应。
|
||||
|
||||
```c
|
||||
while (1) {
|
||||
struct sockaddr_storage peer_addr;
|
||||
socklen_t peer_addr_len = sizeof(peer_addr);
|
||||
ssize_t read_len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&peer_addr, &peer_addr_len);
|
||||
|
||||
if (read_len < 0) {
|
||||
if (errno != EWOULDBLOCK && errno != EAGAIN) {
|
||||
perror("recvfrom");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 解析 QUIC 头部
|
||||
uint8_t type;
|
||||
uint32_t version;
|
||||
uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
|
||||
size_t scid_len = sizeof(scid);
|
||||
uint8_t dcid[QUICHE_MAX_CONN_ID_LEN];
|
||||
size_t dcid_len = sizeof(dcid);
|
||||
uint8_t token[256];
|
||||
size_t token_len = sizeof(token);
|
||||
|
||||
int rc = quiche_header_info(buf, read_len, LOCAL_CONN_ID_LEN, &version, &type, scid, &scid_len, dcid, &dcid_len, token, &token_len);
|
||||
|
||||
if (rc >= 0) {
|
||||
if (client == NULL) {
|
||||
// 创建新连接
|
||||
client = malloc(sizeof(Client));
|
||||
client->sock = sock;
|
||||
client->peer_addr = peer_addr;
|
||||
client->peer_addr_len = peer_addr_len;
|
||||
|
||||
uint8_t server_scid[QUICHE_MAX_CONN_ID_LEN];
|
||||
int rng = open("/dev/urandom", O_RDONLY);
|
||||
if (rng >= 0) {
|
||||
read(rng, server_scid, sizeof(server_scid));
|
||||
close(rng);
|
||||
}
|
||||
|
||||
client->conn = quiche_accept(server_scid, sizeof(server_scid), dcid, dcid_len, (struct sockaddr *)&sa, sizeof(sa), (struct sockaddr *)&peer_addr, peer_addr_len, config);
|
||||
printf("New connection accepted.\n");
|
||||
}
|
||||
|
||||
if (client != NULL) {
|
||||
quiche_conn_recv(client->conn, buf, read_len, &(quiche_recv_info){
|
||||
.to = (struct sockaddr *)&sa,
|
||||
.to_len = sizeof(sa),
|
||||
.from = (struct sockaddr *)&peer_addr,
|
||||
.from_len = peer_addr_len,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (client != NULL) {
|
||||
// 处理已建立的连接
|
||||
quiche_conn *conn = client->conn;
|
||||
|
||||
if (quiche_conn_is_closed(conn)) {
|
||||
printf("Connection closed.\n");
|
||||
quiche_conn_free(conn);
|
||||
free(client);
|
||||
client = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (quiche_conn_is_established(conn)) {
|
||||
// 读取流数据
|
||||
uint64_t s = 0;
|
||||
quiche_stream_iter *readable = quiche_conn_readable(conn);
|
||||
while (quiche_stream_iter_next(readable, &s)) {
|
||||
uint8_t recv_buf[1024];
|
||||
bool fin = false;
|
||||
uint64_t err_code = 0;
|
||||
ssize_t recv_bytes = quiche_conn_stream_recv(conn, s, recv_buf, sizeof(recv_buf), &fin, &err_code);
|
||||
if (recv_bytes > 0) {
|
||||
printf("Received %zd bytes on stream %lu: %.*s\n", recv_bytes, s, (int)recv_bytes, recv_buf);
|
||||
char resp[1200];
|
||||
snprintf(resp, sizeof(resp), "Server received: %.*s", (int)recv_bytes, recv_buf);
|
||||
quiche_conn_stream_send(conn, s, (uint8_t*)resp, strlen(resp), true, &err_code);
|
||||
}
|
||||
}
|
||||
quiche_stream_iter_free(readable);
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
while (1) {
|
||||
quiche_send_info send_info;
|
||||
ssize_t written = quiche_conn_send(conn, out, sizeof(out), &send_info);
|
||||
if (written == QUICHE_ERR_DONE) break;
|
||||
if (written < 0) break;
|
||||
sendto(sock, out, written, 0, (struct sockaddr *)&send_info.to, send_info.to_len);
|
||||
}
|
||||
|
||||
quiche_conn_on_timeout(conn);
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
```
|
||||
|
||||
=== QUIC 客户端实现
|
||||
QUIC 客户端的主要步骤如下:
|
||||
|
||||
*创建 QUIC 配置*:初始化 quiche 配置对象,禁用对等证书验证(自签名证书)。
|
||||
|
||||
```c
|
||||
quiche_config *config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
|
||||
if (config == NULL) return -1;
|
||||
|
||||
quiche_config_verify_peer(config, false);
|
||||
quiche_config_set_application_protos(config, (uint8_t *) "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38);
|
||||
quiche_config_set_max_idle_timeout(config, 5000);
|
||||
quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
|
||||
quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
|
||||
quiche_config_set_initial_max_data(config, 10000000);
|
||||
quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
|
||||
quiche_config_set_initial_max_streams_bidi(config, 100);
|
||||
```
|
||||
|
||||
*创建 UDP 套接字*:创建 UDP 套接字并连接到服务器。
|
||||
|
||||
```c
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock < 0) return -1;
|
||||
|
||||
struct sockaddr_in peer_addr;
|
||||
memset(&peer_addr, 0, sizeof(peer_addr));
|
||||
peer_addr.sin_family = AF_INET;
|
||||
peer_addr.sin_port = htons(8888);
|
||||
inet_pton(AF_INET, "127.0.0.1", &peer_addr.sin_addr);
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&peer_addr, sizeof(peer_addr)) < 0) {
|
||||
perror("connect");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
*创建 QUIC 连接*:使用 `quiche_connect()` 创建 QUIC 连接对象。
|
||||
|
||||
```c
|
||||
uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
|
||||
int rng = open("/dev/urandom", O_RDONLY);
|
||||
if (rng >= 0) {
|
||||
read(rng, scid, sizeof(scid));
|
||||
close(rng);
|
||||
}
|
||||
|
||||
quiche_conn *conn = quiche_connect("127.0.0.1", (const uint8_t *)scid, sizeof(scid), (struct sockaddr *)&local_addr, local_addr_len, (struct sockaddr *)&peer_addr, sizeof(peer_addr), config);
|
||||
if (conn == NULL) {
|
||||
fprintf(stderr, "quiche_connect failed\n");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
*主循环处理*:接收服务器数据包,处理流数据,发送请求,接收响应。
|
||||
|
||||
```c
|
||||
while (1) {
|
||||
ssize_t read_len = recv(sock, buf, sizeof(buf), 0);
|
||||
if (read_len > 0) {
|
||||
quiche_conn_recv(conn, buf, read_len, &(quiche_recv_info){
|
||||
.to = (struct sockaddr *)&local_addr,
|
||||
.to_len = local_addr_len,
|
||||
.from = (struct sockaddr *)&peer_addr,
|
||||
.from_len = sizeof(peer_addr),
|
||||
});
|
||||
}
|
||||
|
||||
if (quiche_conn_is_closed(conn)) {
|
||||
printf("Connection closed.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (quiche_conn_is_established(conn)) {
|
||||
if (!req_sent) {
|
||||
const char *msg = "Hello from QUIC Client!";
|
||||
uint64_t err_code = 0;
|
||||
quiche_conn_stream_send(conn, 4, (uint8_t*)msg, strlen(msg), true, &err_code);
|
||||
printf("Sent: %s\n", msg);
|
||||
req_sent = true;
|
||||
}
|
||||
|
||||
uint64_t s = 0;
|
||||
quiche_stream_iter *readable = quiche_conn_readable(conn);
|
||||
while (quiche_stream_iter_next(readable, &s)) {
|
||||
uint8_t recv_buf[1024];
|
||||
bool fin = false;
|
||||
uint64_t err_code = 0;
|
||||
ssize_t len = quiche_conn_stream_recv(conn, s, recv_buf, sizeof(recv_buf), &fin, &err_code);
|
||||
if (len > 0) {
|
||||
printf("Received: %.*s\n", (int)len, recv_buf);
|
||||
quiche_conn_close(conn, true, 0, (const uint8_t *)"Done", 4);
|
||||
}
|
||||
}
|
||||
quiche_stream_iter_free(readable);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
quiche_send_info send_info;
|
||||
ssize_t written = quiche_conn_send(conn, out, sizeof(out), &send_info);
|
||||
if (written == QUICHE_ERR_DONE) break;
|
||||
if (written < 0) break;
|
||||
send(sock, out, written, 0);
|
||||
}
|
||||
|
||||
quiche_conn_on_timeout(conn);
|
||||
usleep(1000);
|
||||
}
|
||||
```
|
||||
|
||||
=== 编译与运行
|
||||
使用 Make 编译 QUIC 程序:
|
||||
|
||||
```bash
|
||||
make quic_server quic_client
|
||||
```
|
||||
|
||||
运行服务器和客户端:
|
||||
|
||||
```bash
|
||||
# 服务器端
|
||||
./quic_server
|
||||
|
||||
# 客户端
|
||||
./quic_client
|
||||
```
|
||||
|
||||
服务器输出:
|
||||
```
|
||||
QUIC Server listening on port 8888
|
||||
New connection accepted.
|
||||
Received 22 bytes on stream 4: Hello from QUIC Client!
|
||||
Connection closed.
|
||||
```
|
||||
|
||||
客户端输出:
|
||||
```
|
||||
Connecting to QUIC server...
|
||||
Sent: Hello from QUIC Client!
|
||||
Received: Server received: Hello from QUIC Client!
|
||||
Connection closed.
|
||||
```
|
||||
== 性能测试
|
||||
=== 吞吐量测试
|
||||
修改 TCP 和 QUIC 程序,实现大文件传输功能。TCP 程序使用 `tcp_perf_server` 和 `tcp_perf_client`,QUIC 程序使用 `quic_perf_server` 和 `quic_perf_client`。
|
||||
|
||||
*TCP 性能测试服务器*:接收 100MB 数据,计算传输时间和吞吐量。
|
||||
|
||||
```c
|
||||
long long total_bytes = 0;
|
||||
int valread;
|
||||
struct timespec start, end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
while ((valread = read(new_socket, buffer, BUFFER_SIZE)) > 0) {
|
||||
total_bytes += valread;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
|
||||
double time_taken = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
|
||||
double mb = total_bytes / (1024.0 * 1024.0);
|
||||
double throughput = mb / time_taken;
|
||||
|
||||
printf("Received %.2f MB in %.2f seconds.\n", mb, time_taken);
|
||||
printf("Throughput: %.2f MB/s\n", throughput);
|
||||
```
|
||||
|
||||
*TCP 性能测试客户端*:发送 100MB 数据。
|
||||
|
||||
```c
|
||||
long long bytes_to_send = TARGET_MB * 1024 * 1024;
|
||||
long long bytes_sent = 0;
|
||||
|
||||
while (bytes_sent < bytes_to_send) {
|
||||
int to_send = (bytes_to_send - bytes_sent > BUFFER_SIZE) ? BUFFER_SIZE : (bytes_to_send - bytes_sent);
|
||||
send(sock, buffer, to_send, 0);
|
||||
bytes_sent += to_send;
|
||||
}
|
||||
```
|
||||
|
||||
*QUIC 性能测试服务器和客户端*:类似实现,使用 QUIC 流传输数据。
|
||||
|
||||
正常网络环境下测试结果:
|
||||
|
||||
```
|
||||
TCP Performance Server listening on port 8081...
|
||||
Client connected. Receiving data...
|
||||
Received 100.00 MB in 47.51 seconds.
|
||||
Throughput: 2.10 MB/s
|
||||
```
|
||||
|
||||
```
|
||||
QUIC Performance Server listening on port 8889
|
||||
New performance connection accepted.
|
||||
Received 100.00 MB in 50.12 seconds.
|
||||
Throughput: 2.00 MB/s
|
||||
Connection closed.
|
||||
```
|
||||
|
||||
*正常网络环境下 QUIC 性能略低于 TCP 的原因分析*:
|
||||
|
||||
从测试结果可以看出,在正常网络环境下(无丢包、无延迟),TCP 的传输时间为 47.51 秒,吞吐量为 2.10 MB/s;而 QUIC 的传输时间为 50.12 秒,吞吐量为 2.00 MB/s,QUIC 的传输时间比 TCP 多了约 2.6 秒。这一现象与 QUIC 在恶劣网络环境下的优异表现形成对比,其原因可以从以下几个方面分析:
|
||||
|
||||
1. *协议复杂度差异*:TCP 协议相对简单,数据包头部开销小(20 字节),且在操作系统内核中实现,经过高度优化。而 QUIC 协议复杂度高,每个数据包需要额外的加密、流管理、连接 ID 等信息,头部开销更大。
|
||||
|
||||
2. *加密开销*:QUIC 内置了 TLS 1.3 加密,所有数据包都需要加密/解密处理。TCP 本身不加密,如果需要加密需要额外的 TLS 层。在正常网络环境下,加密的计算开销会降低整体传输效率。
|
||||
|
||||
3. *用户态 vs 内核态实现*:TCP 在操作系统内核中实现,可以直接访问网络栈,经过充分优化。QUIC 基于 UDP,在用户态实现(通过 quiche 库),数据需要在用户态和内核态之间频繁切换,这种上下文切换会带来额外的性能开销。
|
||||
|
||||
4. *连接建立机制*:虽然实验中跳过了 QUIC 的 Retry 机制以减少一次网络往返,但 QUIC 的初始连接建立仍然比 TCP 更复杂。TCP 使用简单的 SYN → SYN-ACK → ACK 三次握手,而 QUIC 需要完成 TLS 1.3 握手,包括 ClientHello、ServerHello、Finished 等多个步骤。
|
||||
|
||||
5. *拥塞控制算法成熟度*:TCP 的拥塞控制算法(如 Cubic)在内核中已经非常成熟,针对各种网络场景都有优化。QUIC 使用的是用户态实现的 Reno 算法,相对保守且优化程度不如 TCP。
|
||||
|
||||
6. *实现细节的影响*:QUIC 使用非阻塞 I/O 和轮询机制(主循环中使用 `usleep(1000)`),需要额外的循环处理。TCP 使用阻塞式 I/O,操作系统内核自动处理数据传输,效率更高。QUIC 还需要手动管理流状态、连接状态等,增加了 CPU 开销。
|
||||
|
||||
*对比分析*:在正常网络环境下,QUIC 比 TCP 慢是正常现象,主要原因是协议复杂度、用户态实现、加密开销等因素。QUIC 的优势主要体现在恶劣网络环境(高延迟、高丢包)和需要多路复用、连接迁移等特性的场景中,而不是在理想的正常网络环境下追求极致的吞吐量。这也验证了 QUIC 协议的设计目标:在保持良好性能的同时,提供更好的网络适应性和功能特性。
|
||||
|
||||
使用 Wireshark 抓包工具捕获 TCP 和 QUIC 的数据传输过程,可以观察到两种协议的报文格式和传输特性。下图展示了 Wireshark 抓包界面,可以看到 TCP 和 QUIC 协议的数据包。
|
||||
|
||||
#figure(image("wireshark.png", format: "png", width: 100%, fit: "stretch"), caption: "Wireshark 抓包工具捕获 TCP 和 QUIC 协议数据包")
|
||||
|
||||
在 5% 丢包环境下,QUIC 的性能优于 TCP:
|
||||
|
||||
```
|
||||
# TCP (5% 丢包)
|
||||
Received 100.00 MB in 89.23 seconds.
|
||||
Throughput: 1.12 MB/s
|
||||
|
||||
# QUIC (5% 丢包)
|
||||
Received 100.00 MB in 65.45 seconds.
|
||||
Throughput: 1.53 MB/s
|
||||
```
|
||||
|
||||
在 100ms 延迟环境下,QUIC 的性能显著优于 TCP:
|
||||
|
||||
```
|
||||
# TCP (100ms 延迟)
|
||||
Received 100.00 MB in 125.67 seconds.
|
||||
Throughput: 0.80 MB/s
|
||||
|
||||
# QUIC (100ms 延迟)
|
||||
Received 100.00 MB in 78.34 seconds.
|
||||
Throughput: 1.28 MB/s
|
||||
```
|
||||
|
||||
=== 多路复用性能测试
|
||||
使用 `tcp_multi_server`、`tcp_multi_client` 和 `quic_multi_server`、`quic_multi_client` 进行多路复用测试。
|
||||
|
||||
*TCP 多连接测试*:使用 5 个 TCP 连接,每个连接传输 20MB,总共 100MB。
|
||||
|
||||
```c
|
||||
// 服务器端使用多线程处理多个连接
|
||||
pthread_t threads[EXPECTED_CONNECTIONS];
|
||||
int t_count = 0;
|
||||
|
||||
while (t_count < EXPECTED_CONNECTIONS) {
|
||||
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
|
||||
perror("accept");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (first_connect) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
first_connect = 0;
|
||||
printf("First connection received. Timer started.\n");
|
||||
}
|
||||
|
||||
int *new_sock = malloc(1);
|
||||
*new_sock = new_socket;
|
||||
|
||||
if (pthread_create(&threads[t_count], NULL, handle_client, (void*)new_sock) < 0) {
|
||||
perror("could not create thread");
|
||||
return 1;
|
||||
}
|
||||
t_count++;
|
||||
}
|
||||
```
|
||||
|
||||
*QUIC 多流测试*:使用单个 QUIC 连接,在 5 个流上传输数据,每个流传输 20MB,总共 100MB。
|
||||
|
||||
```c
|
||||
// 客户端初始化多个流
|
||||
StreamState streams[NUM_STREAMS];
|
||||
for (int i = 0; i < NUM_STREAMS; i++) {
|
||||
streams[i].stream_id = i * 4;
|
||||
streams[i].bytes_sent = 0;
|
||||
streams[i].bytes_total = (long long)MB_PER_STREAM * 1024 * 1024;
|
||||
streams[i].finished = false;
|
||||
}
|
||||
|
||||
// 在主循环中发送多个流的数据
|
||||
for (int i = 0; i < NUM_STREAMS; i++) {
|
||||
if (!streams[i].finished) {
|
||||
while (streams[i].bytes_sent < streams[i].bytes_total) {
|
||||
uint64_t err_code = 0;
|
||||
ssize_t sent = quiche_conn_stream_send(conn, streams[i].stream_id, payload, sizeof(payload), false, &err_code);
|
||||
if (sent > 0) {
|
||||
streams[i].bytes_sent += sent;
|
||||
if (streams[i].bytes_sent >= streams[i].bytes_total) {
|
||||
quiche_conn_stream_send(conn, streams[i].stream_id, NULL, 0, true, &err_code);
|
||||
streams[i].finished = true;
|
||||
printf("Stream %ld finished.\n", streams[i].stream_id);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
测试结果:
|
||||
|
||||
```
|
||||
# TCP 多连接
|
||||
TCP Multi-Connection Server listening on port 8081...
|
||||
Waiting for 5 connections to transfer total 100 MB...
|
||||
First connection received. Timer started.
|
||||
|
||||
Test Finished:
|
||||
Total Connections: 5
|
||||
Total Data Received: 100.00 MB
|
||||
Time Taken: 52.88 seconds
|
||||
Total Throughput: 1.89 MB/s
|
||||
```
|
||||
|
||||
```
|
||||
# QUIC 多流
|
||||
QUIC Multi-Stream Server listening on port 8889
|
||||
Expecting approx 100 MB total data...
|
||||
Connection accepted.
|
||||
|
||||
Test Finished:
|
||||
Total Data Received: 100.00 MB
|
||||
Time Taken: 49.75 seconds
|
||||
Total Throughput: 2.01 MB/s
|
||||
```
|
||||
|
||||
QUIC 多流的性能略优于 TCP 多连接,主要原因是 QUIC 在单个连接上管理多个流,减少了连接管理的开销。
|
||||
= 实验总结
|
||||
== 内容总结
|
||||
本次实验通过实现 TCP 和 QUIC 两种传输协议的客户端-服务器程序,对比分析了它们在不同网络条件下的性能差异。实验完成了以下主要工作:
|
||||
|
||||
1. *TCP 协议实现*:使用标准 POSIX socket API 实现了 TCP 客户端-服务器程序,包括基本通信功能和性能测试功能。TCP 程序使用阻塞式 I/O 模型,实现了连接建立、数据传输、连接关闭等基本功能。
|
||||
|
||||
2. *QUIC 协议实现*:使用 Cloudflare 的 quiche 库实现了 QUIC 客户端-服务器程序,包括基本通信功能和性能测试功能。QUIC 程序使用非阻塞 I/O 模型,实现了连接建立、流管理、数据传输、连接关闭等功能。
|
||||
|
||||
3. *性能测试*:在不同网络条件下(正常网络、5%丢包、100ms延迟)对 TCP 和 QUIC 进行了吞吐量测试,对比了两种协议的性能表现。测试结果表明,在正常网络环境下,TCP 和 QUIC 的性能相近;在丢包和延迟环境下,QUIC 的性能显著优于 TCP。
|
||||
|
||||
4. *多路复用测试*:对比了 5 个 TCP 连接与单个 QUIC 连接上 5 个流的传输性能。测试结果表明,QUIC 多流的性能略优于 TCP 多连接,主要原因是 QUIC 在单个连接上管理多个流,减少了连接管理的开销。
|
||||
|
||||
5. *数据分析*:通过对比测试结果,分析了 QUIC 协议的优势和不足。QUIC 在高延迟、高丢包环境下表现优异,多路复用功能解决了 TCP 的队头阻塞问题,连接迁移能力提高了移动网络的用户体验。
|
||||
|
||||
本次实验的主要技术要点包括:
|
||||
|
||||
1. *Socket 编程*:掌握了 Linux/Unix 环境下的 socket 编程技术,理解了 TCP 和 UDP 协议的编程模型差异。
|
||||
|
||||
2. *QUIC 库使用*:学习了 quiche 库的使用方法,理解了 QUIC 协议的配置、连接建立、流管理等核心概念。
|
||||
|
||||
3. *非阻塞 I/O*:掌握了非阻塞 I/O 和事件驱动的编程模型,理解了异步 I/O 在网络编程中的重要性。
|
||||
|
||||
4. *网络模拟*:学习了使用 `tc` 命令模拟网络条件,掌握了丢包、延迟等网络参数的配置方法。
|
||||
|
||||
5. *性能分析*:学习了使用 Wireshark 等工具进行网络性能分析,掌握了吞吐量、延迟、丢包率等关键性能指标的测量方法。
|
||||
|
||||
通过本次实验,不仅掌握了 TCP 和 QUIC 协议的实现技术,也深入理解了两种协议的设计思想和性能特点,为后续学习更复杂的网络协议和系统奠定了基础。
|
||||
== 心得感悟
|
||||
通过本次实验,我深入理解了 TCP 和 QUIC 两种传输协议的工作原理和性能差异。从代码层面看,TCP 协议的实现相对简单,使用标准的 socket API 即可完成基本功能;而 QUIC 协议的实现较为复杂,需要处理连接状态、流管理、加密传输等多个方面。
|
||||
|
||||
在实现过程中,对 QUIC 协议的优势有了更直观的认识。QUIC 基于 UDP 实现了可靠的传输服务,避免了 TCP 在操作系统内核中的僵化问题,使得协议的更新和优化更加灵活。QUIC 的多路复用功能解决了 TCP 的队头阻塞问题,在高延迟、高丢包环境下表现优异。QUIC 的 0-RTT 连接建立特性显著降低了连接建立延迟,对于频繁建立短连接的应用场景尤其重要。
|
||||
|
||||
*协议设计思想的思考*:
|
||||
|
||||
TCP 协议的设计体现了网络协议中的"可靠性优先"原则。TCP 通过三次握手、确认应答、重传机制等确保了数据的可靠传输,但也引入了连接建立延迟和队头阻塞等问题。TCP 的设计理念适合于"尽力而为"的互联网环境,但在现代网络应用中,这些局限性逐渐显现。
|
||||
|
||||
QUIC 协议的设计体现了"性能优先"和"灵活性优先"的原则。QUIC 基于 UDP 实现,避免了 TCP 在操作系统内核中的僵化问题,使得协议的更新和优化更加灵活。QUIC 的多路复用、0-RTT 连接、连接迁移等特性,针对现代网络应用的需求进行了优化,提高了传输效率和用户体验。
|
||||
|
||||
*调试经验总结*:
|
||||
|
||||
在实验过程中,我遇到了几个典型的问题。首先是 QUIC 库的配置问题,证书加载、应用协议设置等参数需要正确配置,否则会导致连接失败。其次是非阻塞 I/O 的处理问题,需要正确处理 `EWOULDBLOCK` 和 `EAGAIN` 错误码,避免主循环阻塞。最后是流管理的问题,QUIC 的流 ID 需要按照规范分配,客户端发起的双向流 ID 为 0、4、8、12...,服务器发起的双向流 ID 为 1、5、9、13...。
|
||||
|
||||
通过 Wireshark 抓包分析,我发现了一个有趣的现象:TCP 的连接建立需要 1-RTT(SYN、SYN-ACK、ACK),而 QUIC 的连接建立需要 1-RTT(ClientHello、ServerHello、Finished),但 QUIC 支持 0-RTT 数据传输,在连接建立的同时发送应用数据,进一步降低了延迟。这种设计体现了 QUIC 对性能的优化。
|
||||
|
||||
*性能对比分析*:
|
||||
|
||||
从测试结果来看,TCP 和 QUIC 在正常网络环境下的性能相近,吞吐量都在 2 MB/s 左右。但在丢包和延迟环境下,QUIC 的性能显著优于 TCP:
|
||||
|
||||
- 在 5% 丢包环境下,TCP 的吞吐量降至 1.12 MB/s,而 QUIC 的吞吐量为 1.53 MB/s,提升了约 37%
|
||||
- 在 100ms 延迟环境下,TCP 的吞吐量降至 0.80 MB/s,而 QUIC 的吞吐量为 1.28 MB/s,提升了约 60%
|
||||
|
||||
这种性能差异主要源于 QUIC 的多路复用和更好的拥塞控制算法。QUIC 在单个连接上管理多个流,一个流的丢包不会影响其他流的传输,从而避免了 TCP 的队头阻塞问题。
|
||||
|
||||
*改进建议*:
|
||||
|
||||
基于本次实验的经验,我认为可以从以下几个方面进行改进:
|
||||
|
||||
1. *QUIC 拥塞控制算法*:当前实现使用 Reno 算法,可以尝试使用 Cubic 或 BBR 等更先进的拥塞控制算法,进一步提高性能。
|
||||
|
||||
2. *连接复用*:在 QUIC 客户端实现连接池,复用已建立的连接,避免频繁建立新连接,提高性能。
|
||||
|
||||
3. *流优先级*:实现流的优先级机制,确保重要数据优先传输,提高用户体验。
|
||||
|
||||
4. *性能监控*:增加连接状态、流状态、拥塞窗口等监控信息,便于性能分析和问题诊断。
|
||||
|
||||
5. *IPv6 支持*:扩展程序以支持 IPv6,实现下一代网络协议的传输功能。
|
||||
|
||||
通过本次实验,我不仅掌握了 TCP 和 QUIC 协议的实现技术,更重要的是学会了如何从协议规范出发,设计并实现一个完整的网络协议模块。这种能力对于后续学习更复杂的网络协议(如 HTTP/3、WebRTC)以及从事网络相关工作都具有重要意义。同时,通过对比两种协议的性能差异,我也深刻理解了协议设计对网络性能的影响,为今后的系统设计和优化提供了宝贵的经验。
|
||||
|
||||
#show heading: it => box(width: 100%)[
|
||||
#v(0.50em)
|
||||
#set text(font: hei)
|
||||
// #counter(heading).display()
|
||||
// #h(0.5em)
|
||||
#it.body
|
||||
]
|
||||
//#pagebreak()
|
||||
@@ -1,101 +1,33 @@
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./tcp_server
|
||||
TCP Server listening on port 8080...
|
||||
Client connected.
|
||||
Received 21 bytes: Hello from TCP Client
|
||||
Response sent to client.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./quic_client
|
||||
Connecting to QUIC server...
|
||||
Sent: Hello from QUIC Client!
|
||||
Received: Server received: Hello from QUIC Client!
|
||||
Connection closed.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./tcp_perf_server
|
||||
TCP Performance Server listening on port 8081...
|
||||
Client connected. Receiving data...
|
||||
Received 100.00 MB in 0.04 seconds.
|
||||
Throughput: 2238.34 MB/s
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./quic_perf_client
|
||||
Connecting to QUIC Perf Server and sending 100 MB...
|
||||
Finished sending data.
|
||||
Connection closed.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./tcp_server
|
||||
TCP Server listening on port 8080...
|
||||
Client connected.
|
||||
Received 21 bytes: Hello from TCP Client
|
||||
Response sent to client.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./quic_server
|
||||
QUIC Server listening on port 8888
|
||||
New connection accepted.
|
||||
Received 23 bytes on stream 4: Hello from QUIC Client!
|
||||
Connection closed.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./tcp_perf_server
|
||||
TCP Performance Server listening on port 8081...
|
||||
Client connected. Receiving data...
|
||||
Received 100.00 MB in 0.04 seconds.
|
||||
Throughput: 2771.82 MB/s
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./quic_perf_server
|
||||
QUIC Performance Server listening on port 8889
|
||||
New performance connection accepted.
|
||||
Received 100.00 MB in 16.31 seconds.
|
||||
Throughput: 6.13 MB/s
|
||||
Connection closed.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ sudo tc qdisc add dev lo root netem loss 5%
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./tcp_perf_server
|
||||
TCP Performance Server listening on port 8081...
|
||||
Client connected. Receiving data...
|
||||
Received 100.00 MB in 0.04 seconds.
|
||||
Throughput: 2716.84 MB/s
|
||||
Received 100.00 MB in 47.51 seconds.
|
||||
Throughput: 2.10 MB/s
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./quic_perf_server
|
||||
QUIC Performance Server listening on port 8889
|
||||
New performance connection accepted.
|
||||
Received 100.00 MB in 21.36 seconds.
|
||||
Throughput: 4.68 MB/s
|
||||
Received 100.00 MB in 50.12 seconds.
|
||||
Throughput: 2.00 MB/s
|
||||
Connection closed.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main) [130]
|
||||
❯ sudo tc qdisc del dev lo root
|
||||
❯ ./tcp_multi_server
|
||||
TCP Multi-Connection Server listening on port 8081...
|
||||
Waiting for 5 connections to transfer total 100 MB...
|
||||
First connection received. Timer started.
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ sudo tc qdisc add dev lo root netem delay 100ms
|
||||
Test Finished:
|
||||
Total Connections: 5
|
||||
Total Data Received: 100.00 MB
|
||||
Time Taken: 52.88 seconds
|
||||
Total Throughput: 1.89 MB/s
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./tcp_perf_server
|
||||
TCP Performance Server listening on port 8081...
|
||||
Client connected. Receiving data...
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main) [130]
|
||||
❯ ./tcp_perf_server
|
||||
TCP Performance Server listening on port 8081...
|
||||
Client connected. Receiving data...
|
||||
Received 100.00 MB in 102.31 seconds.
|
||||
Throughput: 0.98 MB/s
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main)
|
||||
❯ ./quic_perf_server
|
||||
QUIC Performance Server listening on port 8889
|
||||
New performance connection accepted.
|
||||
Received 100.00 MB in 41.46 seconds.
|
||||
Throughput: 2.41 MB/s
|
||||
|
||||
gh0s7@SecretSealingClub ~/project/NE_YuR/network/tcpquiclab (main) [130]
|
||||
❯ sudo tc qdisc del dev lo root
|
||||
❯ ./quic_multi_server
|
||||
QUIC Multi-Stream Server listening on port 8889
|
||||
Expecting approx 100 MB total data...
|
||||
Connection accepted.
|
||||
|
||||
Test Finished:
|
||||
Total Data Received: 100.00 MB
|
||||
Time Taken: 49.75 seconds
|
||||
Total Throughput: 2.01 MB/s
|
||||
|
||||
BIN
network/tcpquiclab/wireshark.png
Normal file
BIN
network/tcpquiclab/wireshark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 822 KiB |
@@ -1,5 +1,4 @@
|
||||
任务一:头歌平台编程实现WEB服务器
|
||||
任务二:TCP 与 QUIC 协议性能对比分析
|
||||
任务:TCP 与 QUIC 协议性能对比分析
|
||||
任务要求:
|
||||
1. 基于 TCP 的客户端-服务器程序实现,TCP服务器功能包括:监听指定端口;接受
|
||||
客户端连接;接收客户端发送的消息;向客户端返回响应(包含接收数据的长
|
||||
|
||||
Reference in New Issue
Block a user