TCP 相关实验
理解 listen 的第⼆个参数
• 基于刚才封装的 TcpSocket 实现以下测试代码
• 对于服务器, listen 的第⼆个参数设置为 1, 并且不调⽤ accept
• 测试代码链接:https://gitee.com/whb-helloworld/linux-plus-meal/tree/master/testbacklog
test_server.cc
cpp
#include "tcp_socket.hpp"
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage ./test_server [ip] [port]\n");
return 1;
}
TcpSocket sock;
bool ret = sock.Bind(argv[1], atoi(argv[2]));
if (!ret)
{
return 1;
}
ret = sock.Listen(2);
if (!ret)
{
return 1;
}
// 客⼾端不进⾏ accept
while (1)
{
sleep(1);
}
return 0;
}
test_client.cc
cpp
#include "tcp_socket.hpp"
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage ./test_client [ip] [port]\n");
return 1;
}
TcpSocket sock;
bool ret = sock.Connect(argv[1], atoi(argv[2]));
if (ret)
{
printf("connect ok\n");
}
else
{
printf("connect failed\n");
}
while (1)
{
sleep(1);
}
return 0;
}
此时启动 3 个客⼾端同时连接服务器, ⽤ netstat 查看服务器状态, ⼀切正常.
但是启动第四个客⼾端时, 发现服务器对于第四个连接的状态存在问题了
bash
tcp 3 0 0.0.0.0:9090 0.0.0.0:* LISTEN
9084/./test_server
tcp 0 0 127.0.0.1:9090 127.0.0.1:48178 SYN_RECV
-
tcp 0 0 127.0.0.1:9090 127.0.0.1:48176
ESTABLISHED -
tcp 0 0 127.0.0.1:48178 127.0.0.1:9090
ESTABLISHED 9140/./test_client
tcp 0 0 127.0.0.1:48174 127.0.0.1:9090
ESTABLISHED 9087/./test_client
tcp 0 0 127.0.0.1:48176 127.0.0.1:9090
ESTABLISHED 9088/./test_client
tcp 0 0 127.0.0.1:48172 127.0.0.1:9090
ESTABLISHED 9086/./test_client
tcp 0 0 127.0.0.1:9090 127.0.0.1:48174
ESTABLISHED -
tcp 0 0 127.0.0.1:9090 127.0.0.1:48172
ESTABLISHED -
客⼾端状态正常, 但是服务器端出现了 SYN_RECV 状态, ⽽不是 ESTABLISHED 状态
这是因为, Linux内核协议栈为⼀个tcp连接管理使⽤两个队列:
-
半链接队列(⽤来保存处于SYN_SENT和SYN_RECV状态的请求)
-
全连接队列(accpetd队列)(⽤来保存处于established状态,但是应⽤层没有调⽤accept取⾛的请求)
⽽全连接队列的⻓度会受到 listen 第⼆个参数的影响.
全连接队列满了的时候, 就⽆法继续让当前连接的状态进⼊ established 状态了.
这个队列的⻓度通过上述实验可知, 是 listen 的第⼆个参数 + 1.
使⽤TCP dump进⾏抓包,分析TCP过程
测试的时候要注意,注意和代码结合哦,我们代码中故意在close(sockfd)哪⾥留了⼀个问题
TCPDump 是⼀款强⼤的⽹络分析⼯具,主要⽤于捕获和分析⽹络上传输的数据包。
安装 tcpdump
tcpdump通常已经预装在⼤多数 Linux 发⾏版中。如果没有安装,可以使⽤包管理器进⾏安装。例如 Ubuntu,可以使⽤以下命令安装:
bash
sudo apt-get update
sudo apt-get install tcpdump
在 Red Hat 或 CentOS 系统中,可以使⽤以下命令:
bash
sudo yum install tcpdump
常⻅使⽤
1. 捕获所有⽹络接⼝上的 TCP 报⽂
使⽤以下命令可以捕获所有⽹络接⼝上传输的 TCP 报⽂:
bash
$ sudo tcpdump -i any tcp
注意: -i any 指定捕获所有⽹络接⼝上的数据包, tcp 指定捕获 TCP 协议的数据包。 i 可以理解成为 interface 的意思
2. 捕获指定⽹络接⼝上的 TCP 报⽂
如果你只想捕获某个特定⽹络接⼝(如 eth0)上的 TCP 报⽂,可以使⽤以下命令:
bash
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.45.153 netmask 255.255.192.0 broadcast 172.18.63.255
inet6 fe80::216:3eff:fe03:959b prefixlen 64 scopeid 0x20<link>
ether 00:16:3e:03:95:9b txqueuelen 1000 (Ethernet)
RX packets 34367847 bytes 9360264363 (9.3 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 34274797 bytes 6954263329 (6.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ sudo tcpdump -i eth0 tcp
3. 捕获特定源或⽬的 IP 地址的 TCP 报⽂
使⽤ host 关键字可以指定源或⽬的 IP 地址。例如,要捕获源 IP 地址为 192.168.1.100的 TCP 报⽂,可以使⽤以下命令:
bash
$ sudo tcpdump src host 192.168.1.100 and tcp
要捕获⽬的 IP 地址为 192.168.1.200 的 TCP 报⽂,可以使⽤以下命令:
bash
$ sudo tcpdump dst host 192.168.1.200 and tcp
同时指定源和⽬的 IP 地址,可以使⽤ and 关键字连接两个条件:
bash
$ sudo tcpdump src host 192.168.1.100 and dst host 192.168.1.200 and tcp
4. 捕获特定端⼝的 TCP 报⽂
使⽤ port 关键字可以指定端⼝号。例如,要捕获端⼝号为 80 的 TCP 报⽂(通常是 HTTP 请求),可以使⽤以下命令:
bash
$ sudo tcpdump port 80 and tcp
5. 保存捕获的数据包到⽂件
使⽤ -w 选项可以将捕获的数据包保存到⽂件中,以便后续分析。例如:
bash
$ sudo tcpdump -i eth0 port 80 -w data.pcap
这将把捕获到的 HTTP 流量保存到名为 data.pcap 的⽂件中。
• 了解:pcap后缀的⽂件通常与PCAP(Packet Capture)⽂件格式相关,这是⼀种⽤于捕获⽹络数据包的⽂件格式
6. 从⽂件中读取数据包进⾏分析
使⽤ -r 选项可以从⽂件中读取数据包进⾏分析。例如:
bash
tcpdump -r data.pcap
这将读取 data.pcap ⽂件中的数据包并进⾏分析。
注意事项
• 使⽤ tcpdump时,请确保你有⾜够的权限来捕获⽹络接⼝上的数据包。通常,你需要以 root ⽤⼾⾝份运⾏ tcpdump。
• 使⽤tcpdump的时候,有些主机名会被云服务器解释成为随机的主机名,如果不想要,就⽤-n选项
• 主机观察三次握⼿的第三次握⼿,不占序号
使⽤ wireshark 分析 TCP 通信流程(了解)
wireshark是 windows 下的⼀个⽹络抓包⼯具. 虽然 Linux 命令⾏中有 tcpdump ⼯具同样能完成抓
包, 但是 tcpdump 是纯命令⾏界⾯, 使⽤起来不如 wireshark ⽅便.
下载 wireshark
https://1.na.dl.wireshark.org/win64/Wireshark-win64-2.6.3.exe
或者
链接:https://pan.baidu.com/s/159UUIoZ8b7guWDeuAHoF9A
提取码:k79r
安装 wireshark
直接双击安装, 没啥太多注意的.
启⽤ telnet 客⼾端
参考 https://jingyan.baidu.com/article/95c9d20d96ba4aec4f756154.html
启动 wireshark 并设置过滤器
由于机器上的⽹络数据报可能较多, 我们只需要关注我们需要的. 因此需要设置过滤器
在过滤器栏中写⼊
bash
ip.addr == [服务器 ip]
则只抓取指定ip的数据包.

或者在过滤器中写⼊
bash
tcp.port == 9090
则只关注 9090 端⼝的数据
更多过滤器的设置, 参考
https://blog.csdn.net/donot_worry_be_happy/article/details/80786241
观察三次握⼿过程
启动好服务器.
使⽤ telnet 作为客⼾端连接上服务器
bash
telnet [ip] [port]
抓包结果如下:

观察三个报⽂各⾃的序列号和确认序号的规律.
在中间部分可以看到 TCP 报⽂详细信息

观察确认应答
在 telnet 中输⼊⼀个字符

可以看到客⼾端发送⼀个⻓度为 1 字节的数据, 此时服务器返回了⼀个 ACK 以及⼀个 9 个字节的响应(捎带应答), 然后客⼾端再反馈⼀个 ACK(注意观察 序列号和确认序号)
观察四次挥⼿
在 telnet 中输⼊ ctrl + ], 回到 telnet 控制界⾯, 输⼊ quit 退出

实际上是 "三次挥⼿", 由于捎带应答, 导致其中的两次重合在了⼀起.
注意事项
如果使⽤虚拟机部署服务器, 建议使⽤ "桥接⽹卡" 的⽅式连接⽹络. NAT ⽅式下由于进⾏了 ip 和 port 的替换.
使⽤云服务器测试, 更加直观⽅便.
附录:

