43.Linux网络编程--TCP IP原理深入剖析

前几节我们说了一下我们TCP/IP的一些发展历史,分层及各层一些关键的协议的一个简单介绍,然后详细的进行了TCP,UDP的一个网络编程。想要进一步的深入理解,编程上更游刃有余的话,需要详细的分析一下TCP/IP协议的一些关键机制。

 

一. wirshark 工具

学习网络编程的人,必然会接触这个工具,毕竟网络协议栈太大,太复杂,不抓包分析一下,又怎么能理解的了其中的原理。wireshark是非常流行的网络封包分析软件,功能十分强大。可以截取各种网络封包,显示网络封包的详细信息。

安装过程我们就不说了,安装之后打开软件。

43.Linux网络编程--TCP IP原理深入剖析

然后点击Caputre->Interfaces.. 出现下面对话框,选择正确的网卡。然后点击"Start"按钮, 开始抓包。(我们的虚拟机会虚拟出个网卡)

43.Linux网络编程--TCP IP原理深入剖析

我们可以通过设置显示过滤器来选择我们想要监控的程序。

过滤器(显示过滤器和捕获过滤器)会帮助我们在大量的数据中迅速找到我们需要的信息。

 

下面我们就详细说一下过滤器的规则:

1.过滤ip(来源ip或者目标IP等于某个ip)

例:

  • ip.src eq 192.168.0.129 
  • ip.dst eq 192.168.0.129
  • ip.addr eq 192.168.0.129 

 

2.过滤端口

例:

  • tcp.port eq 80 // 不管端口是来源的还是目标的都显示
  • tcp.port == 80
  • tcp.port eq 80 or udp.port eq 80
  • tcp.dstport == 80 // 只显tcp协议的目标端口80
  • tcp.srcport == 80 // 只显tcp协议的来源端口80

过滤端口范围

tcp.port >= 1 and tcp.port <= 80

 

3.过滤协议

例:

ip

udp

tcp

http

 

4.过滤MAC

  • eth.dst == A0:00:00:04:C5:84 // 过滤目标mac
  • eth.src eq A0:00:00:04:C5:84 // 过滤来源mac
  • eth.dst==A0:00:00:04:C5:84
  • eth.dst==A0-00-00-04-C5-84
  • eth.addr eq A0:00:00:04:C5:84 // 过滤来源MAC和目标MAC都等于A0:00:00:04:C5:84

 

5.包长度过滤

例子:

  • udp.length == 26 这个长度是指udp本身固定长度8加上udp下面数据包之和
  • tcp.len >= 7   指的是TCP下面数据包,不包括tcp本身
  • ip.len == 94 除了以太网头固定长度14,其它都算是ip.len,即从ip本身到最后

 

6.http模式过滤

 

7.tcp参数过滤

  • tcp.flags 显示包含TCP标志的封包。
  • tcp.flags.syn == 0x02     显示包含TCP SYN标志的封包

 

8.包内容过滤

等等还有很多,再详细的我们百度,用到哪个查哪个,一口吃不了个胖子,学linux一定要适可而止,每天知识点内容都太多了。

 

注意我们的抓取过程,在windows下运行wirshark,抓取本地虚拟机与远程linux通讯的包的过程。

43.Linux网络编程--TCP IP原理深入剖析

如果是本地的虚拟机内通讯没经过网卡,我们windows下肯定是抓不到包。

 

 

二. 详细讲解各个层级的包头

回故一下这是我们网络中的封包和拆包的过程。从这个过程中,我们可以看到要增加很多的包头,类似于我们快递,每传递到一个地,要加一个地址一样。虽然图中写的是以太网头啊,ip头啊,说的简单,其实每个头部内部又有很多的字节和很多的含义来沟通,下面我们就分别进行这些头部内部的一个说明。

 

43.Linux网络编程--TCP IP原理深入剖析

TCP/IP 协议网络封包格式:

43.Linux网络编程--TCP IP原理深入剖析

2.1  以太网头格式(14字节)

43.Linux网络编程--TCP IP原理深入剖析

目的地址和源地址都是MAC地址。

 

类型:TCP,UDP http等。

 

2.2 IP头(20字节)

43.Linux网络编程--TCP IP原理深入剖析

TOS:包括优先级字段,最小时延,最大吞吐量,最高可靠性和最小费用,其实说白了就是在网络拥堵的时候,它决定传谁。(给你的服务是vip,还是普通用户)

 

标识字段:标识字段唯一地标识主机发送的每一份数据报。通常每发送一份报文它的值就会加1(处理大数据包时,用于拆包,接收机通过这些标识进行重组)

这个过程挺复杂也挺有意思的可以详细的分析下,并且这个概念也经常出题,问IP头中标识字段的作用。

 

生存时间:T T L(time-to-live)生存时间字段设置了数据报可以经过的最多路由器数。

 

2.3 TCP头(20字节)

43.Linux网络编程--TCP IP原理深入剖析

网络传输层中,TCP是面向连接、可靠的、字节流传输。

端口号:作用就是我从哪里来,又要到哪里去。

32位序号:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。

32位确认号:用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1。

4位头部长度:标识该TCP头部有多少个32bit字(4字节)。

6位标志位:

  • URG标志:紧急指针(urgent pointer)是否有效。
  • ACK标志:确认号是否有效。我们称携带ACK标识的TCP报文段为确认报文段。
  • PSH标志:提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间(如果应用程序不将
  • 接收到的数据读走,它们就会一直停留 在TCP接收缓冲区中)。
  • RST标志:要求对方重新建立连接。我们称携带RST标志的TCP报文段为复位报文段。
  • SYN标志:请求建立一个连接。我们称携带SYN标志的TCP报文段为同步报文段。
  • FIN标志:通知对方本端要关闭连接了。我们称携带FIN标志的TCP报文段为结束 报文段。

16位窗口大小:是TCP流量控制的一个手段。

16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法以检验 TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。

TCP头部选项:TCP头部的最后一个选项字段(options)是可变长的可选信息。这部分最多包含40字节

 

这样我们就知道了这些字节的含义,但是其实我还是不太明白他们到底是啥?通过这么几个字节作为基础实现了复杂的逻辑,确实还是非常不好理解的。下面我们会简单的分析一下其中一些重要的原理,帮助我们加深一下理解。

 

2.4 UDP头(8字节)

43.Linux网络编程--TCP IP原理深入剖析

三. TCP内关键机制详细分析

我们都说TCP是可靠传输,那么它是怎么做到可靠的呢?

TCP中主要是通过以下几个特性保证了数据传输的可靠:

(1)***和确认应答信号

(2)超时重发控制

(3)连接管理

(4)滑动窗口控制

(5)流量控制

(6)拥塞控制

 

3.1.超时重发机制

其实在我们平时的开发中,想要保证数据可靠也经常用到类似的简单的机制。

本地机A发送一包数据给目标机器B,此时A上设置一个超时时间。如果在超时时间内B没有给A返回应答包,我们就认为他没收到,然后A将数据包重发。

其实在这个过程中有可能是B没收到,也有可能B返回应答包了,因为一些原因应答包丢失或者延时到达。A很任性,认为反正我没收到,就要不断重发,使B不断的收到重复包这个过程非常浪费资源。

 

为了帮助B不接收重复的包,就引入了***的概念

***是按照顺序给发送数据的每一个字节(8位字节)都标上号码的编号。接收端查询接收数据 TCP 首部中的***和数据的长度,将自己下一步应该接收的***作为确认应答返送回去。通过***和确认应答号,TCP 能够识别是否已经接收数据,又能够判断是否需要接收,从而实现可靠传输。

 

刚才我们也提到了要设置一个超时重发时间,时间没到就重发,但是这个特定的时间间隔我们怎么得到呢?理想状态肯定是这个时间段又能干了活,还要保证这个时间理论最短。

 

TCP中每次发包时都会计算往返时间(RTT Round Trip Time)及其偏差(RTT波动的时间,也叫抖动)。将这个往返时间和偏差时间相加,重发超时的时间就是比这个总和要稍大一点的值。

同时网络在重发的过程中数据也不会被无限、反复地重发。达到一定重发次数之后,如果仍没有任何确认应答返回,就会判断为网络或对端主机发生了异常,强制关闭连接。并且通知应用通信异常强行终止。

 

3.2  连接管理:

TCP是面向连接的通信协议,面向连接是指在数据通信之前先做好通信两端之间的准备工作。
因此,在数据通信之前,会通过TCP首部发送一个SYN包作为建立连接和等待确认应答,如果对端发来确认应答ACK,则认为可以进行通信,否则如果对端没有发送正确的ACK应答,那么就不会通信。另外通信完毕需要发送FIN包来关闭连接

这就是我们常常说的 三次握手建立连接 和四次挥手关闭连接

43.Linux网络编程--TCP IP原理深入剖析

三次握手:

(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。

(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。

(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

 

四次挥手:

(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。

(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

 

从上面我们可看出,有了可靠的建立连接过程(三次握手),及可靠的 断开过程(4次挥手),然后在发送过程又有***的超时重发机制。头,尾,身体都有了,好像我们的TCP机制学完了呢。其实我们还忽略了一个重要的东西就是我们的数据包。在传输大数据包的时候我们该怎么办,肯定是分包呗,但是一分包处理机制就更加复杂了。那么每次传输的分包长度是多少呢?

 

3.3数据包的发送,滑动窗口机制(提高数据传输效率)

TCP是以段为单位进行数据包的发送的在建立 TCP 连接的同时,也可以确定发送数据包的单位,我们也可以称其为“最大消息长度”(MSS,Max Segment Size),也就是一个段。最理想的情况是,最大消息长度正好是 IP 中不会被分片处理(上面IP头中说的,数据包太大会被碎片化处理的最大数据长度。

TCP 在传送大量数据时,是以 MSS 的大小将数据进行分割发送。进行重发时也是以 MSS 为单位。

MSS 在三次握手的时候,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在 TCP 首部中写入 MSS 选项,告诉对方自己的接口能够适应的 MSS 的大小。然后会在两者之间选择一个较小的值投入使用。

但是分包之后,每发送一段数据就进行一次确认应答,那么包的往返时间就会加长,那么性能肯定就越低了。过程如

下图所示:

43.Linux网络编程--TCP IP原理深入剖析

然后就引入了窗口的机制,那他的实现原理是什么?有点像单线程变成了多线程处理,发送一段数据后,不用一直等待,接着发下一段,一直到最大的窗口大小后在进行应答处理。(使用了大量的缓冲区对数据进行缓存,然后对多段同时进行应答确认)

窗口大小:无需等待确认应答ACK而继续发送数据的最大值。

优化后如下图:

43.Linux网络编程--TCP IP原理深入剖析

以下是滑动窗口的示意图:图看不懂??不要紧。

43.Linux网络编程--TCP IP原理深入剖析

假如我们设置的窗口大小是4000字节,1段是1000个字节也就是发送四包后才对ACK进行处理。

 

正常情况:小A有四个包裹编号1,2,3,4,小A递给小B 1号包裹,2号包裹,3号包裹,4号包裹。过程中不管小B和我说啥,我都听不见,当我递完4号包裹后,才会腾出耳朵来听小B和我说啥(因为你之前和我约定好了要专心,每递完4包再听我说话),此时听见小B说,给我第5个包裹,那么我就认为你的前4个包裹都收到了,开始第5个包裹的传递,这样当然是极好的。

异常情况:假如传递过程中出现了问题,小B没收到2号包裹,就会告诉小A,我要2号包裹,然而小A听不见啊,接着给第3个包裹,小B看到3号包裹后大怒,傻叉我要的是2号包裹,但是小A还是听不见啊,又发了4号,小B一看竟然还不是2号包裹,我不要。告诉小A我就要2号包裹。此时小A发完四个包裹,终于能听见声音了,发现小B说给我第2个包裹,小A才意识到,我擦,2,3,4都丢了,只能返回到2的位置,将2,3,4,按顺序重新给小B发一遍。

注:你会发现你设置的这个窗口类似于设置个缓冲区,只要人家没确认收到,你就要随时准备从缓存的指定位置读取出来给人家重传,收到了之后才能开始向下滑动,清理之前的数据缓存。以此类推可靠的完成所有数据包的传输。

 

如果我们的窗口大小设置的比较大,其中一包丢失了,别人都给你喊了100次了,你还一直给别人发不想要的也不行啊,于是又优化了机制。

高速重发控制:在滑动窗口比较大的情况下,同一个***的确认应答将会被重复不断地返回。而发送端主机如果 连续 3 次 接收到同一个确认应答包,就会将其对应的数据重发,这种机制比之前提到的“超时重发”更加高效,所以被称之为“高速重发控制”(喊你三次发错了,不纠正我就给你一棒子,我才不管之前的约定的是啥呢)

每个头的组成和含义我们都明确了,此时通过工具抓出一个完整的包,我们就可以对着含义进行理解了。