文章

制作一个基于.NET原生Socket的WEB API转发工具(1) - TCP协议

Socket编程是网络编程中的重中之重,.NET为我们提供了丰富,详细的类库为我们使用C#进行Socket编程提供帮助,这里作为学习,会尽可能使用相对基础的Socket类库,来完成整个程序的构建,首先我们来了解Socket,因为是直接学习Socket,对于更底层的协议不会推进得那么深入彻底,主要会介绍一些相对重要的概念,先了解TCP协议

了解TCP协议

先看结论

TCP协议是面向连接的,可靠的传输层协议

什么是连接

这里的连接不是指我们用网线进行的物理的连接,而是指一种逻辑上的连接,双方可以通过某种方式,访问到对方的资源,就称之为连接。那么为什么说这个连接是可靠的呢,为了了解,或者说印证这个结论,先让我们了解TCP的报文格式

TCP报文

报文(message),用百度百科上的话来说

报文(message)是网络中交换与传输的数据单元,即站点一次性要发送的数据块。报文包含了将要发送的完整的数据信息,其长短很不一致,长度不限且可变。

通俗的说,就是通信双方在通信中每次要发送的数据,这个整体,我们称之为报文,我们先来看一个TCP报文的格式

从左上部分到右下部分依次开始了解:

Source Port & Destination Port:端口号 - 源计算机上应用程序的端口号和目标计算机上应用程序的端口号,都分别占16位

Sequence Number:序列号 - 32位无符号整数,在TCP传输中,传输的字节流中的每个字节都会被编号,当SYN(后面会介绍什么是SYN)为1时,即建立连接时,这个字段的值是一个初始值,也被称为ISN(initialization sequence number),当SYN不为1时,是传输数据段中第一个字节的序列号

Acknowledgement Number:确认号 - 也是32位无符号整数,表示接受方期望收到发送方下一个报文数据段的第一个字节数据的编号,其值是接收计算机即将接收到的下一个序列号

Data Offset:数据偏移量 - 占4位,指数据段中的需要的数据部分起始处距离TCP数据段的字节偏移量,告诉接收端的应用程序,数据从何处开始

Reserved:保留字段,占4位,为TCP将来的发展预留空间,目前必须全部为0

标志位字段(关于标志位字段的定义这里是完全从网络资料复制的,因为对前面三个标志位体会还不是很深):

  • CWR(Congestion Window Reduce):拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率。
  • ECE(ECN Echo):用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵。
  • URG(Urgent):表示本报文段中发送的数据是否包含紧急数据。URG=1 时表示有紧急数据。当 URG=1 时,后面的紧急指针字段才有效。
  • ACK:表示前面的确认号字段是否有效。ACK=1 时表示有效。只有当 ACK=1 时,前面的确认号字段才有效。TCP 规定,连接建立后,ACK 必须为 1。
  • PSH(Push):告诉对方收到该报文段后是否立即把数据推送给上层。如果值为 1,表示应当立即把数据提交给上层,而不是缓存起来。
  • RST:表示是否重置连接。如果 RST=1,说明 TCP 连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。
  • SYN:在建立连接时使用,用来同步序号。当 SYN=1,ACK=0 时,表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时,表示对方同意建立连接。SYN=1 时,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才为 1。
  • FIN:标记数据是否发送完毕。如果 FIN=1,表示数据已经发送完成,可以释放连接。

窗口大小字段

窗口大小(Window Size):占 16 位。它表示从 Ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间。该字段可以用于 TCP 的流量控制。

TCP 校验和字段

校验位(TCP Checksum):占 16 位。它用于确认传输的数据是否有损坏。发送端基于数据内容校验生成一个数值,接收端根据接收的数据校验生成一个值。两个值必须相同,才能证明数据是有效的。如果两个值不同,则丢掉这个数据包。Checksum 是根据伪头 + TCP 头 + TCP 数据三部分进行计算的。

紧急指针字段

紧急指针(Urgent Pointer):仅当前面的 URG 控制位为 1 时才有意义。它指出本数据段中为紧急数据的字节数,占 16 位。当所有紧急数据处理完后,TCP 就会告诉应用程序恢复到正常操作。即使当前窗口大小为 0,也是可以发送紧急数据的,因为紧急数据无须缓存。

可选项字段

选项(Option):长度不定,但长度必须是 32bits 的整数倍。

讲到这里,大家肯定还是对这些报文在TCP连接中有什么用,它们为什么使TCP连接变得可靠有疑惑,接下来我们会对这个过程进行详细分析,在分析之前,先让我们简单了解一个概念,HTTP协议

HTTP协议(摘自知乎回答)

HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接1。从建立连接到关闭连接的过程称为“一次连接”。

抓包分析

根据前文的知识,我们已经知道,HTTP是一个基于TCP的应用层协议,在一次正常的HTTP请求的开始到结束,都会完成建立连接,数据传输,释放连接这一完整的过程,那么我们只需要分析一个完整的HTTP请求,再进行分析,就可以得出相应的结论了

抓包分析,顾名思义,就是对网络传输过程中的一些数据包进行抓取分析,为了用尽量简单的工具和尽量快捷的步骤来完成这个分析过程,我们在一台CentOS的服务器上完成这个抓包分析的过程

首先,我们使用tcpdump命令,这是一个可以帮助我们对网络数据包进行抓取分析的命令

1
tcpdump -nn -X -S -i eth0 port 80

命令中用到的参数:-nn:在展示结果中显示目标IP地址和端口号,-X:tcpdump 会打印每个包的头部数据, 同时会以16进制和ASCII码形式打印出每个包的数据,-S:在抓包的内容中会显示绝对序列号而不是相对序列号,-i:指定要监视的网卡,默认只会监听第一个网卡,port:指定抓取源或者目的端口是80的包

使用这条命令后,会进入到抓取监视界面,此时我们打开一个新的窗口,使用curl命令,这个命令帮助我们对www.douban.com发出一次GET请求

1
curl www.douban.com

此时,查看tcpdump抓包结果(这边直接贴上来应该会看得眼花,如果看不清,建议粘贴到VSCode等可以全屏显示的地方,或者自己操作一遍,也能体会得更深刻)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
01:40:16.950097 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [S], seq 470463866, win 29200, options [mss 1460,sackOK,TS val 120066337 ecr 0,nop,wscale 7], length 0
        0x0000:  4500 003c a45b 4000 4006 b7e0 c0a8 0024  E..<.[@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b57a 0000 0000  .......P...z....
        0x0020:  a002 7210 deae 0000 0204 05b4 0402 080a  ..r.............
        0x0030:  0728 1121 0000 0000 0103 0307            .(.!........
01:40:16.979743 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [S.], seq 1101183244, ack 470463867, win 43440, options [mss 1440,sackOK,TS val 599874893 ecr 120066337,nop,wscale 9], length 0
        0x0000:  4500 003c 0000 4000 2f06 6d3c 9a08 83ab  E..<..@./.m<....
        0x0010:  c0a8 0024 0050 a3f0 41a2 b90c 1c0a b57b  ...$.P..A......{
        0x0020:  a012 a9b0 b604 0000 0204 05a0 0402 080a  ................
        0x0030:  23c1 5d4d 0728 1121 0103 0309            #.]M.(.!....
01:40:16.979758 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [.], ack 1101183245, win 229, options [nop,nop,TS val 120066367 ecr 599874893], length 0
        0x0000:  4500 0034 a45c 4000 4006 b7e7 c0a8 0024  E..4.\@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b57b 41a2 b90d  .......P...{A...
        0x0020:  8010 00e5 dea6 0000 0101 080a 0728 113f  .............(.?
        0x0030:  23c1 5d4d                                #.]M
01:40:16.979812 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [P.], seq 470463867:470463945, ack 1101183245, win 229, options [nop,nop,TS val 120066367 ecr 599874893], length 78: HTTP: GET / HTTP/1.1
        0x0000:  4500 0082 a45d 4000 4006 b798 c0a8 0024  E....]@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b57b 41a2 b90d  .......P...{A...
        0x0020:  8018 00e5 def4 0000 0101 080a 0728 113f  .............(.?
        0x0030:  23c1 5d4d 4745 5420 2f20 4854 5450 2f31  #.]MGET./.HTTP/1
        0x0040:  2e31 0d0a 5573 6572 2d41 6765 6e74 3a20  .1..User-Agent:.
        0x0050:  6375 726c 2f37 2e32 392e 300d 0a48 6f73  curl/7.29.0..Hos
        0x0060:  743a 2077 7777 2e64 6f75 6261 6e2e 636f  t:.www.douban.co
        0x0070:  6d0d 0a41 6363 6570 743a 202a 2f2a 0d0a  m..Accept:.*/*..
        0x0080:  0d0a                                     ..
01:40:17.009390 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [.], ack 470463945, win 85, options [nop,nop,TS val 599874923 ecr 120066367], length 0
        0x0000:  4500 0034 e586 4000 2f06 87bd 9a08 83ab  E..4..@./.......
        0x0010:  c0a8 0024 0050 a3f0 41a2 b90d 1c0a b5c9  ...$.P..A.......
        0x0020:  8010 0055 8d90 0000 0101 080a 23c1 5d6b  ...U........#.]k
        0x0030:  0728 113f                                .(.?
01:40:17.009393 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [P.], seq 1101183245:1101183620, ack 470463945, win 85, options [nop,nop,TS val 599874923 ecr 120066367], length 375: HTTP: HTTP/1.1 301 Moved Permanently
        0x0000:  4500 01ab e587 4000 2f06 8645 9a08 83ab  E.....@./..E....
        0x0010:  c0a8 0024 0050 a3f0 41a2 b90d 1c0a b5c9  ...$.P..A.......
        0x0020:  8018 0055 4fb5 0000 0101 080a 23c1 5d6b  ...UO.......#.]k
        0x0030:  0728 113f 4854 5450 2f31 2e31 2033 3031  .(.?HTTP/1.1.301
        0x0040:  204d 6f76 6564 2050 6572 6d61 6e65 6e74  .Moved.Permanent
        0x0050:  6c79 0d0a 4461 7465 3a20 5375 6e2c 2030  ly..Date:.Sun,.0
        0x0060:  3220 4175 6720 3230 3230 2031 373a 3430  2.Aug.2020.17:40
        0x0070:  3a31 3620 474d 540d 0a43 6f6e 7465 6e74  :16.GMT..Content
        0x0080:  2d54 7970 653a 2074 6578 742f 6874 6d6c  -Type:.text/html
        0x0090:  0d0a 436f 6e74 656e 742d 4c65 6e67 7468  ..Content-Length
        0x00a0:  3a20 3136 320d 0a43 6f6e 6e65 6374 696f  :.162..Connectio
        0x00b0:  6e3a 206b 6565 702d 616c 6976 650d 0a4b  n:.keep-alive..K
        0x00c0:  6565 702d 416c 6976 653a 2074 696d 656f  eep-Alive:.timeo
        0x00d0:  7574 3d33 300d 0a4c 6f63 6174 696f 6e3a  ut=30..Location:
        0x00e0:  2068 7474 7073 3a2f 2f77 7777 2e64 6f75  .https://www.dou
        0x00f0:  6261 6e2e 636f 6d2f 0d0a 5365 7276 6572  ban.com/..Server
        0x0100:  3a20 6461 650d 0a0d 0a3c 6874 6d6c 3e0d  :.dae....<html>.
        0x0110:  0a3c 6865 6164 3e3c 7469 746c 653e 3330  .<head><title>30
        0x0120:  3120 4d6f 7665 6420 5065 726d 616e 656e  1.Moved.Permanen
        0x0130:  746c 793c 2f74 6974 6c65 3e3c 2f68 6561  tly</title></hea
        0x0140:  643e 0d0a 3c62 6f64 793e 0d0a 3c63 656e  d>..<body>..<cen
        0x0150:  7465 723e 3c68 313e 3330 3120 4d6f 7665  ter><h1>301.Move
        0x0160:  6420 5065 726d 616e 656e 746c 793c 2f68  d.Permanently</h
        0x0170:  313e 3c2f 6365 6e74 6572 3e0d 0a3c 6872  1></center>..<hr
        0x0180:  3e3c 6365 6e74 6572 3e6e 6769 6e78 3c2f  ><center>nginx</
        0x0190:  6365 6e74 6572 3e0d 0a3c 2f62 6f64 793e  center>..</body>
        0x01a0:  0d0a 3c2f 6874 6d6c 3e0d 0a              ..</html>..
01:40:17.009406 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [.], ack 1101183620, win 237, options [nop,nop,TS val 120066396 ecr 599874923], length 0
        0x0000:  4500 0034 a45e 4000 4006 b7e5 c0a8 0024  E..4.^@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b5c9 41a2 ba84  .......P....A...
        0x0020:  8010 00ed dea6 0000 0101 080a 0728 115c  .............(.\
        0x0030:  23c1 5d6b                                #.]k
01:40:17.009480 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [F.], seq 470463945, ack 1101183620, win 237, options [nop,nop,TS val 120066396 ecr 599874923], length 0
        0x0000:  4500 0034 a45f 4000 4006 b7e4 c0a8 0024  E..4._@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b5c9 41a2 ba84  .......P....A...
        0x0020:  8011 00ed dea6 0000 0101 080a 0728 115c  .............(.\
        0x0030:  23c1 5d6b                                #.]k
01:40:17.039006 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [F.], seq 1101183620, ack 470463946, win 85, options [nop,nop,TS val 599874953 ecr 120066396], length 0
        0x0000:  4500 0034 e588 4000 2f06 87bb 9a08 83ab  E..4..@./.......
        0x0010:  c0a8 0024 0050 a3f0 41a2 ba84 1c0a b5ca  ...$.P..A.......
        0x0020:  8011 0055 8bdc 0000 0101 080a 23c1 5d89  ...U........#.].
        0x0030:  0728 115c                                .(.\
01:40:17.039013 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [.], ack 1101183621, win 237, options [nop,nop,TS val 120066426 ecr 599874953], length 0
        0x0000:  4500 0034 a460 4000 4006 b7e3 c0a8 0024  E..4.`@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b5ca 41a2 ba85  .......P....A...
        0x0020:  8010 00ed dea6 0000 0101 080a 0728 117a  .............(.z
        0x0030:  23c1 5d89                                #.].

来,让我们先冷静一下,我们可以看到,这些消息是分段的,每一个片段以时间,IP开头,每一段就是我们刚刚介绍的TCP报文,从上往下看,我们先看这一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
01:40:16.950097 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [S], seq 470463866, win 29200, options [mss 1460,sackOK,TS val 120066337 ecr 0,nop,wscale 7], length 0
        0x0000:  4500 003c a45b 4000 4006 b7e0 c0a8 0024  E..<.[@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b57a 0000 0000  .......P...z....
        0x0020:  a002 7210 deae 0000 0204 05b4 0402 080a  ..r.............
        0x0030:  0728 1121 0000 0000 0103 0307            .(.!........
01:40:16.979743 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [S.], seq 1101183244, ack 470463867, win 43440, options [mss 1440,sackOK,TS val 599874893 ecr 120066337,nop,wscale 9], length 0
        0x0000:  4500 003c 0000 4000 2f06 6d3c 9a08 83ab  E..<..@./.m<....
        0x0010:  c0a8 0024 0050 a3f0 41a2 b90c 1c0a b57b  ...$.P..A......{
        0x0020:  a012 a9b0 b604 0000 0204 05a0 0402 080a  ................
        0x0030:  23c1 5d4d 0728 1121 0103 0309            #.]M.(.!....
01:40:16.979758 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [.], ack 1101183245, win 229, options [nop,nop,TS val 120066367 ecr 599874893], length 0
        0x0000:  4500 0034 a45c 4000 4006 b7e7 c0a8 0024  E..4.\@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b57b 41a2 b90d  .......P...{A...
        0x0020:  8010 00e5 dea6 0000 0101 080a 0728 113f  .............(.?
        0x0030:  23c1 5d4d                                #.]M

首先,每一段的开头的形如IP 192.168.0.36.41968 > 154.8.131.171.80这样格式的内容,包含了我们之前在报文中提到的源端口和目标端口,这里还显示了IP地址,不过我们需要知道的是,真实的TCP报文中是不会携带IP地址的,这是属于网络层的IP数据报包含的内容。这里这段话告诉我们,这个TCP报文,是从IP地址192.168.0.3641968端口发往154.8.131.17180端口,即从我们的客户端发往豆瓣

上面贴出来的内容分三段,我们主要解析从Flags开始的内容:

  1. 首先Flags中的中括号代表每次传输时的标志位,这里是标志位是SYN2,即SYN = 1,ACK = 03,代表发起了一次新建连接的请求,seq是序列号,在首次传输时,这里初始化了一个随机序列号4470463866,也就是我们之前提到的ISN

  2. 接着,豆瓣向我们返回了一个报文,标志位是SYN + ACKSYN表示豆瓣也和我们建立起一个连接,ACK表示我们之前的报文已经确认送达了,这里的seq是豆瓣那边初始化的随机序列号1101183244,并且报文中ack,即确认号,为之前我们携带的那个序列号 + 1,即470463866 + 1 = 4704638675

  3. 最后,我们这边向豆瓣回复了一个报文,标志位为ACK,确认号为1101183244 + 1 = 1101183245,确认收到了来自豆瓣的连接请求,连接建立

以上的过程,就是我们经常说的,TCP的三次握手,用一张图来表示

再来看中间的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
01:40:16.979812 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [P.], seq 470463867:470463945, ack 1101183245, win 229, options [nop,nop,TS val 120066367 ecr 599874893], length 78: HTTP: GET / HTTP/1.1
        0x0000:  4500 0082 a45d 4000 4006 b798 c0a8 0024  E....]@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b57b 41a2 b90d  .......P...{A...
        0x0020:  8018 00e5 def4 0000 0101 080a 0728 113f  .............(.?
        0x0030:  23c1 5d4d 4745 5420 2f20 4854 5450 2f31  #.]MGET./.HTTP/1
        0x0040:  2e31 0d0a 5573 6572 2d41 6765 6e74 3a20  .1..User-Agent:.
        0x0050:  6375 726c 2f37 2e32 392e 300d 0a48 6f73  curl/7.29.0..Hos
        0x0060:  743a 2077 7777 2e64 6f75 6261 6e2e 636f  t:.www.douban.co
        0x0070:  6d0d 0a41 6363 6570 743a 202a 2f2a 0d0a  m..Accept:.*/*..
        0x0080:  0d0a                                     ..
01:40:17.009390 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [.], ack 470463945, win 85, options [nop,nop,TS val 599874923 ecr 120066367], length 0
        0x0000:  4500 0034 e586 4000 2f06 87bd 9a08 83ab  E..4..@./.......
        0x0010:  c0a8 0024 0050 a3f0 41a2 b90d 1c0a b5c9  ...$.P..A.......
        0x0020:  8010 0055 8d90 0000 0101 080a 23c1 5d6b  ...U........#.]k
        0x0030:  0728 113f                                .(.?
01:40:17.009393 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [P.], seq 1101183245:1101183620, ack 470463945, win 85, options [nop,nop,TS val 599874923 ecr 120066367], length 375: HTTP: HTTP/1.1 301 Moved Permanently
        0x0000:  4500 01ab e587 4000 2f06 8645 9a08 83ab  E.....@./..E....
        0x0010:  c0a8 0024 0050 a3f0 41a2 b90d 1c0a b5c9  ...$.P..A.......
        0x0020:  8018 0055 4fb5 0000 0101 080a 23c1 5d6b  ...UO.......#.]k
        0x0030:  0728 113f 4854 5450 2f31 2e31 2033 3031  .(.?HTTP/1.1.301
        0x0040:  204d 6f76 6564 2050 6572 6d61 6e65 6e74  .Moved.Permanent
        0x0050:  6c79 0d0a 4461 7465 3a20 5375 6e2c 2030  ly..Date:.Sun,.0
        0x0060:  3220 4175 6720 3230 3230 2031 373a 3430  2.Aug.2020.17:40
        0x0070:  3a31 3620 474d 540d 0a43 6f6e 7465 6e74  :16.GMT..Content
        0x0080:  2d54 7970 653a 2074 6578 742f 6874 6d6c  -Type:.text/html
        0x0090:  0d0a 436f 6e74 656e 742d 4c65 6e67 7468  ..Content-Length
        0x00a0:  3a20 3136 320d 0a43 6f6e 6e65 6374 696f  :.162..Connectio
        0x00b0:  6e3a 206b 6565 702d 616c 6976 650d 0a4b  n:.keep-alive..K
        0x00c0:  6565 702d 416c 6976 653a 2074 696d 656f  eep-Alive:.timeo
        0x00d0:  7574 3d33 300d 0a4c 6f63 6174 696f 6e3a  ut=30..Location:
        0x00e0:  2068 7474 7073 3a2f 2f77 7777 2e64 6f75  .https://www.dou
        0x00f0:  6261 6e2e 636f 6d2f 0d0a 5365 7276 6572  ban.com/..Server
        0x0100:  3a20 6461 650d 0a0d 0a3c 6874 6d6c 3e0d  :.dae....<html>.
        0x0110:  0a3c 6865 6164 3e3c 7469 746c 653e 3330  .<head><title>30
        0x0120:  3120 4d6f 7665 6420 5065 726d 616e 656e  1.Moved.Permanen
        0x0130:  746c 793c 2f74 6974 6c65 3e3c 2f68 6561  tly</title></hea
        0x0140:  643e 0d0a 3c62 6f64 793e 0d0a 3c63 656e  d>..<body>..<cen
        0x0150:  7465 723e 3c68 313e 3330 3120 4d6f 7665  ter><h1>301.Move
        0x0160:  6420 5065 726d 616e 656e 746c 793c 2f68  d.Permanently</h
        0x0170:  313e 3c2f 6365 6e74 6572 3e0d 0a3c 6872  1></center>..<hr
        0x0180:  3e3c 6365 6e74 6572 3e6e 6769 6e78 3c2f  ><center>nginx</
        0x0190:  6365 6e74 6572 3e0d 0a3c 2f62 6f64 793e  center>..</body>
        0x01a0:  0d0a 3c2f 6874 6d6c 3e0d 0a              ..</html>..
01:40:17.009406 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [.], ack 1101183620, win 237, options [nop,nop,TS val 120066396 ecr 599874923], length 0
        0x0000:  4500 0034 a45e 4000 4006 b7e5 c0a8 0024  E..4.^@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b5c9 41a2 ba84  .......P....A...
        0x0020:  8010 00ed dea6 0000 0101 080a 0728 115c  .............(.\
        0x0030:  23c1 5d6b                                #.]k
  1. 首先,我们向豆瓣发送了一个标志位为PSHACK的报文,注意看这里包开始有内容和length,我们先看内容,可以看到这里是一个HTTP的GET请求,length,也就是数据的字节长度是78seq的值是470463867:470463945,代表这个报文的数据的首字节的序号是470463867,接收方期望接收到的下一个报文的首字节的序号是4704639456ack的值依然是1101183245,表示希望接收到的豆瓣的下一个包的seq1101183245
  2. 接着,豆瓣回复了我们一个报文,标志位为ACKack值是470463945,里面没有任何数据,代表豆瓣接收到了我们的报文,并且希望下一个收到的我们的报文的首字节序列号是470463945
  3. 然后,豆瓣又发送给我们一个标志位为PSHACK的报文,length是375seq1101183245:1101183620ack470463945,内容中可以看到,豆瓣响应并返回了我们一个HTTP报文,里面含有HTTP报头和一些HTML内容,通过之前的分析,我们可以用一句话来概括,这个报文的首字节序列号是1101183245,豆瓣希望接收到的下一个包的首字节序列号是470463945,不出意外的话,我们回复给豆瓣的ack值应该是1101183620
  4. 最后,我们回复给豆瓣一个标志位为ACK的报文,ack的值是1101183620,表示我们收到了豆瓣的报文

这个,就是数据发送与接收的过程,我们依然用一个图来表示

看最后的一段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
01:40:17.009480 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [F.], seq 470463945, ack 1101183620, win 237, options [nop,nop,TS val 120066396 ecr 599874923], length 0
        0x0000:  4500 0034 a45f 4000 4006 b7e4 c0a8 0024  E..4._@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b5c9 41a2 ba84  .......P....A...
        0x0020:  8011 00ed dea6 0000 0101 080a 0728 115c  .............(.\
        0x0030:  23c1 5d6b                                #.]k
01:40:17.039006 IP 154.8.131.171.80 > 192.168.0.36.41968: Flags [F.], seq 1101183620, ack 470463946, win 85, options [nop,nop,TS val 599874953 ecr 120066396], length 0
        0x0000:  4500 0034 e588 4000 2f06 87bb 9a08 83ab  E..4..@./.......
        0x0010:  c0a8 0024 0050 a3f0 41a2 ba84 1c0a b5ca  ...$.P..A.......
        0x0020:  8011 0055 8bdc 0000 0101 080a 23c1 5d89  ...U........#.].
        0x0030:  0728 115c                                .(.\
01:40:17.039013 IP 192.168.0.36.41968 > 154.8.131.171.80: Flags [.], ack 1101183621, win 237, options [nop,nop,TS val 120066426 ecr 599874953], length 0
        0x0000:  4500 0034 a460 4000 4006 b7e3 c0a8 0024  E..4.`@.@......$
        0x0010:  9a08 83ab a3f0 0050 1c0a b5ca 41a2 ba85  .......P....A...
        0x0020:  8010 00ed dea6 0000 0101 080a 0728 117a  .............(.z
        0x0030:  23c1 5d89                                #.].
  1. 首先,我们往豆瓣发送了一个标志位为FINACK的报文,首字节序列号是470463945,确认号是1101183620,代表我们收到了豆瓣前面传送的报文,并且表示数据已经发送完毕,可以释放连接
  2. 接着,豆瓣也向外面发送了一个标志位为FINACK的报文,首字节序列号是1101183620,确认号是470463946(这里为什么+1,在前文建立连接的时候有过注释),也向我们表示可以释放连接
  3. 最后,我们这边回复给豆瓣一个标志位为ACK的报文,确认号是1101183621,至此,连接完全关闭

一些常见的问题:

  1. 为什么需要握手,为什么是三次

根据RFC793上的描述,之所以需要握手来建立连接,因为TCP需要seq序列号来做可靠重传或接收,为此需要互相确认对方的ISN

那么为什么是三次呢,如果是两次行不行,客户端发送SYN告知服务器自己的ISN,服务器再返回SYN + ACK,确认客户端的序列号并告知客户端自己的ISN,很显然,这样没有客户端还没确认服务器的ISN,双方并没有建立起可靠的连接,如果是四次行不行呢,客户端SYN - 服务器ACK - 服务器 SYN - 客户端ACK,肯定是可以的,之所以是三次,是因为服务器ACK+服务器SYN可以合并成一个步骤,也就是说,最少要三次握手

2.为什么需要挥手,为什么是四次挥手,为什么这次抓包是三次挥手

为什么需要挥手呢,所谓挥手,其实也就是断开连接,其实不难理解握手是一个双方建立连接的过程,在这个过程中双方会在彼此的操作系统中开辟一些资源用于传输用(初始化一些结构,缓冲区等),使得双方可以通过某种连接访问到对方的资源,而挥手,就是释放连接,释放资源的过程。

那么为什么是四次挥手呢,因为TCP是全双工(Full Duplex)的,所谓全双工,就是指可以同时进行信号的双向传输,因此当第一次挥手:客户端第一次发送含有FIN的报文给服务器时,仅仅代表客户端不会再发报文给服务器了,但服务器仍然可以接收报文;第二次挥手:服务器在收到客户端含有FIN的报文时,可能还有数据报文需要发送给客户端,为了让客户端不会因为没有及时收到应答继续发送FIN报文,所以先发送ACK报文告知客户端已经收到其要断开连接的请求;第三次挥手:服务器处理完所有要处理的报文之后,发送FIN报文给客户端,并进入LAST_ACK状态;第四次挥手:客户端发送ACK报文,服务器释放连接

那么为什么这次的抓包结果是客户端FIN + ACK - 服务器FIN + ACK - 客户端 ACK这样,三次挥手呢,因为这边服务器在接收到客户端的FIN报文之后也是直接关闭,没有任何后续操作的,所以这里FINACK没有必要进行间隔,直接合并在一起发送了

Socket是什么

这部分本来打算单独写一篇,但是因为需要特别理解的,和值写的不多,就合并到这里来了

概念浅析

Socket直译成“孔”或“插座”,作为进程之间的一种通信机制,通常也被称为“套接字”,是用于描述IP地址和端口号的通信链的句柄。

要满足套接字这个概念,必须给出四个元组,一端的Ip+port 和另一端的ip+port,是在整个互联网中唯一表示的,唯一表示互联网中两个程序之间的通信 ,操作系统可以分配的最大的端口号是65535

问:当一台服务器已经和另一台服务器建立了65536个(0号端口也能用)连接之后,还能不能和其它服务器建立连接

答:能,因为连往的IP是不同的,还是能唯一表示不同的通信

问:如果服务器就一台,另一台物理服务器要对其进行一个压力测试,测试其并发数,要怎么做

答:使用虚拟IP,不断用不同的IP去与其建立连接

通俗的讲,人与人之间可以通过打电话来进行通信,程序与程序之间要进行通信,互相收发数据,就需要通过Socket来通信,所以不难理解为什么需要描述IP地址和端口号,把电话区号当作IP地址,后面的部分当作端口号,这样构成的一个完整的 电话号码,才能和确切的某一个人进行通话

在这个过程中,协议可以理解成打电话时双方沟通时用的语言,这篇里简单了解,TCP协议是面向连接的,是流式(STREAM)的,每次传输都需要建立三次握手,确保通信过程不会出现数据丢失

UDP协议是无连接的,是数据报式(DATAGRAM)的,不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高,在传输视频的时候通常使用UDP

其实这里的知识,在了解TCP的时候已经有很多可以体会到了,比如为什么必须有四元组,为什么TCP是面向连接的可靠的传输协议,不过因为是先写了一部分这篇,才去写第一篇关于TCP的知识,所以这一部分还是留下来了,不想太过于深究TCP知识,直接看这里也可以

  1. 从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。 

  2. 工具中的标志位显示的是简写,不难看出,SSYN.ACKFFIN 

  3. 有哪个标志位,在数据内容上的表示,就是将哪个标志位置为1,否则的话则为0,也就是不带这个标志位 

  4. 为什么初始序列号需要随机,其实想想不难明白,主要有两个原因,如果初始序列号是硬编码1.假如由于各种原因,有一个包在网络中停留太久,本次连接已经结束了,又建立了一个新的连接,在新的连接建立完之后,这个包才到达目标地址上的端口,此时序列号就很容易和新建立起的序列号撞上,正确的数据包就可能被判定为重复包而被丢弃;2.容易被伪造IP地址的他人,通过使用一样的序列号发包 

  5. 在回复SYNFIN报文的时候,为什么要把确认号设置为收到的报文的seq + 1作为ack,而不直接用seq呢,收到的报文数据长度是0呀,而且直接用seq也能标识,这边找了许多资料,最终找到了一个比较信服的说法:SYNs and FINs require acknowledgement, thus they increment the stream’s sequence number by one when used.For example, if the connection is closed without sending any more data, then if the FIN did not consume a sequence number the closing end couldn’t tell the difference between an ACK for the FIN, and an ACK for the data that was sent prior to the FIN. 意思是说,SYNFIN之所以会将序列号增加一个,是需要做确认,例:如果确认FIN之后,seq没有加1,则无法区分哪些是关闭前发送的数据,哪些是关闭之后发送的数据 

  6. 注意这里期望接收到的下一个包的首字节的序号这个说法,通过观察这里的首字节号和长度不难看出,最后一个字节的字节号不是后面这个数字,所以这个数字代表的是接收方的ack值,其实这个值在报文里面是不会携带的,报文中携带的seq只是数据包第一个字节的序号,这里之所以有是工具为了方便我们查看前后关系 

本文由作者按照 CC BY 4.0 进行授权