龙空技术网

TCP/IP协议栈及网络基础,协议栈原理及实现

程序员yt 182

前言:

目前我们对“tcpip网络编程原理与技术”大约比较讲究,小伙伴们都想要了解一些“tcpip网络编程原理与技术”的相关资讯。那么小编也在网络上收集了一些有关“tcpip网络编程原理与技术””的相关资讯,希望小伙伴们能喜欢,各位老铁们一起来学习一下吧!

1. TCP/IP协议栈及网络基础1.1 OSI网络模型

OSI网路模型分为7层,7层模型及其意义如下图所示:

OSI模型每一层都使用自己的协议,OSI模型每一层都利用下层提供的服务与对等层通信。

OSI每层之间数据报文进行交互的格式如下:

1.2 TCP/IP网络模型

OSI网络模型在程序开发中较为臃肿,各个层次在实现的功能上有所重复,因此TCP/IP协议栈对OSI模型进行了精简,只剩下了5个层次。

TCP/IP网络模型的5个层次分别是物理层,数据链路层,网络层,传输层和应用层,以下对5个层次依次说明。

1.2.1 物理层

物理层的主要作用是把数字信号转变为电信号然后在传输介质中进行传输,物理层处理的数据为二进制数据,也就是bit流。

物理层还描述了传输介质的特性,如传输速率,全双工和半双工等。

常见的传输介质有网线,光纤,无线频段等。

物理层传输的电信号时没有进行分组的,没有规定开头和结尾,无法识别其中的意义,这就需要数据链路层来解决了。

1.2.2 数据链路层

数据链路层定义了电信号分组的标准,使用了相关的通信协议来控制物理层数据的传输。把实现这些协议的硬件和软件加到链路上,就构成了数据链路。

现在最常用的方法是使用适配器(即网卡)来实现这些协议的硬件和软件,一般的适配器都包括了数据链路层和物理层这两层的功能。

数据链路层的传输单元是帧Frame。封装成帧(framing)就是在一段数据的前后分别添加首部和尾部,然后就构成了一个帧。首部和尾部的一个重要作用就是进行帧定界。

数据帧的封装示意图如下:

MTU为最大传输单元,也就是一个数据帧数据部分的最大字节数,在以太网上一般为1500字节,超过1500字节的数据包就需要进行分片传输。

数据链路层常见的协议标准有以太网,PPP等。

1.2.2.1 以太网协议

以太网为局域网中使用最为广泛的数据链路层协议。在有线以太网中,数据的收发需要遵循CSMA/CD协议,而在无线局域网中,数据的收发需要遵循CSMA/CA协议。

MAC地址在以太网中,每个主机或网卡都有一个硬件地址,称为MAC地址。MAC地址是一个全球唯一的地址。共有48bit组成,其中前24bit由IEEE注册管理机构分配给设备厂商的OUI地址,后24位则由厂商自行编码,保证MAC地址无重复。以太网的帧格式如下:

主要包括目的MAC地址,源MAC地址,类型,数据部分和FCS校验和。

其中类型用于标识数据部分的协议类型,其中ip协议为0x0800,ARP协议对应的Type值为0x0806。

设备收到数据帧的处理流程网卡(适配器)从网络上收到的 MAC 帧会使用用硬件检查 MAC 帧中的目的 MAC 地址,如果是发往本站的帧则收下,然后再进行其他的处理。否则就将此帧丢弃,不再进行其他的处理。以太网中的帧类型以太网中共有三种帧类型:单播帧:即目的MAC地址是一个主机的单播MAC地址,只有该主机会收到该报文。组播帧:目的MAC为一个组播的MAC地址,只有一个组播域中的主机才会收到并处理。广播帧:目的MAC地址为全1,表示广播域中的所有主机都会收到。1.2.2.2 vlan概述

交换机的每个端口都是一个冲突域,都需要遵循之前提到的CSMA/CD协议,交换机可以隔离冲突域,但是无法隔离广播域,交换机对收到的广播包会进行泛洪处理,即转发给除了收到报文端口的其他所有端口,当网路中存在大量的ARP报文,例如ARP攻击时就会对一个广播域的中的所有主机造成影响,因此我们需要在局域网中缩写广播域。

vlan,虚拟局域网,可以隔离广播域,主要作用如下:

有效控制广播域范围灵活构建虚拟工作组增强局域网的安全性

vlan使用tag来区分不同的vlan值,共有1-4096个vlan可以使用,带有vlan的数据帧格式为:

1.2.3 网络层

网络层用于隔离广播域,并引入了IP地址的概念。

网络层主要作用如下:

标识节点和链路用唯一的IP地址标识每一个节点用唯一的IP网络号标识每一个链路寻址和转发确定节点所在网络的位置,进而确定节点所在的位置IP路由器选择适当的路径将IP包转发到目的节点适应各种数据链路根据链路的MTU对IP包进行分片和重组为了通过实际的数据链路传递信息,须建立IP地址到数据链路层地址的映射

关于C/C++ Linux后端开发网络底层原理知识 后台私信【架构】 获取,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。

1.2.3.1 ipv4数据包格式

一个 IP 数据报由首部和数据两部分组成。

ip首部的前一部分是固定长度,共 20 字节,是所有 IP 数据报必须具有的。在首部的固定部分的后面是一些可选字段,其长度是可变的,最少占用20字节,不足时会自动填充。

ip数据包没有对数据部分做校验,数据部分在传输层和数据链路层中会进行校验。

ip首部的格式如下:

版本号占 4 位,指 IP 协议的版本,对于ipv4,版本号为4首部长度占 4 位,可表示的最大数值是 15 个单位(一个单位为 4 字节),因此 IP 的首部长度的最大值是 60 字节。可选项部分最小数值为5个单位,就是20个字节。服务类型即区分服务TOS,用于区分不同类型的数据包,例如区分高优先级数据包和低优先级数据包,只有在使用区分服务(DiffServ)时,这个字段才起作用。早期只使用这个字段的前3位,即最多8个数据类型,称为Cos;后来对服务类型字段进行了重新定义,使用前6位做分区服务,称为DSCP,可以标记64中ip数据类型。数据包长度指的是ip数据报文的总长度,包括ip首部和ip数据部分,单位是字节,占用16bit,最大可表示的报文长度为65535字节,不过一般会根据MTU进行分片传输。使用数据包长度减去首部长度就可以得到ip报文的数据有效载荷的大小。标识符标识符通常与标志和片偏移量一起用于数据包的分片。因为在数据链路层规定了MTU值,即ip数据包的最大长度,因此超过MTU值的数据包就需要被分片发送,然后在接收端对这些分片的报文进行重组,得到完整的数据包。标识符的作用就是在一个数据包进行分片后,对这些分片的数据包打上同一个标识符,这样接收端就可以识别出这是属于同一个数据包的分片,然后进行重组。标志标志共有3位,其中第1位未使用,第二位为不分段位(DF位)。当DF设置为1时,表示数据包不允许被分片,若超过MTU值,路由器会因为不能分片而丢弃该数据包。DF位为0则代表该数据包可以被分片。第3位是MF位,MF位置1表示这个分片的数据包不是报文的分片的最后一个报文,MF位置0表示这个数据包是分片的最后一个数据包。片偏移片偏移用于指明分段的起始位置相对于报头起始位置的偏移量,用于接收者根据片偏移量对数据包进行重组。如果一个数据包中的某个分段在网络中丢失,这个数据包需要被整个重新分段和发送。片偏移以 8 个字节为偏移单位,片偏移的示意图如下:生存时间生存

时间即TTL值,标识数据包在网络中传输的最大条数,数据包每经过一个路由器,TTL值就会减1,当TTL值减为0时,该数据包就会被丢弃并向源点发送错误信息,主要用于预防网络中的环路。

TTL字段共8位,最大可以表示255。不同的操作系统定义的TTL值都不一样,一般linux系统定义的TTL值为64,而windows定义的TTL值为128。

使用ping命令时会显示出来从对接收到的报文剩余的TTL值。

#ping linux时返回的TTL值[root@xuzhichao ~]# ping 192.168.20.17PING 192.168.20.17 (192.168.20.17) 56(84) bytes of data.64 bytes from 192.168.20.17: icmp_seq=1 ttl=64 time=0.031 ms64 bytes from 192.168.20.17: icmp_seq=2 ttl=64 time=0.069 ms^C--- 192.168.20.17 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1002msrtt min/avg/max/mdev = 0.031/0.050/0.069/0.019 ms#ping windows时返回的TTL值[root@xuzhichao ~]# ping 192.168.20.1PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data.64 bytes from 192.168.20.1: icmp_seq=1 ttl=128 time=0.234 ms64 bytes from 192.168.20.1: icmp_seq=2 ttl=128 time=0.534 ms^C--- 192.168.20.1 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1005msrtt min/avg/max/mdev = 0.234/0.384/0.534/0.150 ms
#linux中定义TTL值的文件为/proc/sys/net/ipv4/ip_default_ttl[root@xuzhichao ~]# cat /proc/sys/net/ipv4/ip_default_ttl 64
协议

协议字段表明ip上层数据使用的何种协议的包,常见的协议号如下:

头部校验和

用于校验ip数据包的首部字段,路由器收到数据包后会对该数据的包的首部字段进行校验,将校验得到的校验值和此处填充的校验值进行比较,如果一致表明数据包是合法的,如果不一致表明数据包不合法,会将其丢弃。

头部校验和只校验ip的数据包头部,不会校验数据部分。

因为数据传输过程中TTL值一致在变化,因此每个路由器都需要重新计算数据包的校验和。

源地址和目的地址

表示数据包的源ip地址和目的ip地址,数据包在传输过程中源ip地址和目的ip地址一般不会发生变化(NAT场景除外)。

1.2.4 传输层

网络层可以实现主机与主机之间的通信,而传输层可以完成不同主机之间进程的通信。通过TCP/UDP的源端口和目的端口标记两个主机上的不同进程。

运输层为应用进程之间提供端到端的逻辑通信(网络层是为主机之间提供逻辑通信)。

运输层还要对收到的报文进行差错检测。

运输层需要有两种不同的运输协议,即面向连接的 TCP 和无连接的 UDP。

传输层中有端口号的概念,端口号用于区分应用层不同应用程序,例如http使用TCP/80端口,https使用TCP/443端口,SNMP使用UDP/161端口,DNS同时使用TCP/53和UDP/53端口等。

1.2.5 应用层

应用层是为用户提供应用的,通常为应用程序,例如HTTP协议,FTP协议,QQ等。

1.2.6 数据的封装和解封装

主机A发送数据时数据的封装过程如下:

主机B接收数据时数据的解封装过程如下:

在网络中数据的传输过程示意图如下:

主机A对数据包从应用层开始封装,依次向下封装到物理层,在电路上传输时依靠物理层进行传输;经过路由器时会进行ip寻址和转发,路由器接封装到依靠网络层,依据目的ip地址进行转发;经过交换机时,数据会解封装到数据链路层,交换机依据目的MAC地址进行转发;数据达到主机B时,主机B会对报文接封装到应用层,然后处理报文。

1.3 ARP协议

地址解析协议(Address Resolution Protocol),其基本功能为透过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。

什么我们不直接使用硬件地址进行通信?由于全世界存在着各式各样的网络,它们使用不同的硬件地址。要使这些异构网络能够互相通信就必须进行非常复杂的硬件地址转换工作,因此几乎是不可的事。连接到因特网的主机都拥有统一的IP 地址,它们之间的通信就像连接在同一个网络上那样简单方便,因为调用 ARP 来寻找某个路由器或主机的硬件地址都是由计算机软件自动进行的,对用户来说是看不见这种调用过程的。1.3.1 ARP的工作流程:

假设主机ABC在同一个网段,主机A要向主机C发送信息,具体的地址解析过程如下:

(1) 主机A首先查看自己的ARP表,确定其中是否包含有主机C对应的ARP表项。如果找到了对应的MAC地址,则主机A直接利用ARP表中的MAC地址,对IP数据包进行帧封装,并将数据包发送给主机C。

(2) 如果主机A在ARP表中找不到对应的MAC地址,则将缓存该数据报文,然后以广播方式发送一个ARP请求报文。ARP请求报文中的发送端IP地址和发送端MAC地址为主机A的IP地址和MAC地址,目标IP地址和目标MAC地址为主机C的IP地址和全0的MAC地址。由于ARP请求报文以广播方式发送,该网段上的所有主机(B和C)都可以接收到该请求,但只有被请求的主机(即主机C)会对该请求进行处理。

(3) 主机C比较自己的IP地址和ARP请求报文中的目标IP地址,当两者相同时进行如下处理:将ARP请求报文中的发送端(即主机A)的IP地址和MAC地址存入自己的ARP表中。之后以单播方式发送ARP响应报文给主机A,其中包含了自己的MAC地址。

(4) 主机A收到ARP响应报文后,将主机C的MAC地址加入到自己的ARP表中以用于后续报文的转发,同时将IP数据包进行封装后发送出去。

linux上查看本机的arp表项可以使用arp命令:

[root@xuzhichao ~]# arp -nAddress                  HWtype  HWaddress           Flags Mask            Iface192.168.2.182            ether   68:a0:3e:12:10:fa   C                     eth0192.168.20.1             ether   00:50:56:c0:00:01   C                     eth1192.168.2.17                     (incomplete)                              eth0192.168.2.249            ether   c2:89:70:82:aa:44   C                     eth0192.168.2.1              ether   74:7d:24:bb:37:9b   C                     eth0

windows上使用arp -a命令查看。

1.3.2 ARP报文格式

ARP报文是直接封装在链路汇聚层之上的。

ARP报文的ARP 报文总长度为 28 字节,格式如下:

其中,每个字段的含义如下。

硬件类型:指明了发送方想知道的硬件接口类型,以太网的值为 1。协议类型:表示要映射的协议地址类型。它的值为 0x0800,表示 IP 地址。硬件地址长度和协议长度:分别指出硬件地址和协议的长度,以字节为单位。对于以太网上 IP 地址的ARP请求或应答来说,它们的值分别为 6 和 4。操作类型:用来表示这个报文的类型,ARP 请求为 1,ARP 响应为 2,RARP 请求为 3,RARP 响应为 4。发送方 MAC 地址:发送方设备的硬件地址。发送方 IP 地址:发送方设备的 IP 地址。目标 MAC 地址:接收方设备的硬件地址。目标 IP 地址:接收方设备的IP地址。

ARP 数据包分为请求包和响应包,对应报文中的某些字段值也有所不同。

ARP 请求包报文的操作类型(op)字段的值为 request(1),目标 MAC 地址字段的值为 Target 00:00:00:00:00:00(广播地址),数据链路层中的源MAC是发送方的MAC地址,目的MAC地址是广播地址FF:FF:FF:FF:FF:FF。ARP 响应包报文中操作类型(op)字段的值为 reply(2),目标 MAC 地址字段的值为目标主机的硬件地址。数据链路层中的源MAC是响应方的MAC地址,目的MAC地址是请求方的MAC地址。arp请求报文:arp响应报文:1.3.3 免费ARP

免费 ARP 报文与普通 ARP 请求报文的区别在于报文中的目标 IP 地址。普通 ARP 报文中的目标 IP 地址是其他主机的 IP 地址;而免费 ARP 的请求报文中,目标 IP 地址是自己的 IP 地址。

免费ARP(Gratuitous ARP)的主要作用是:

该类型报文起到一个宣告作用。它以广播的形式将数据包发送出去,不需要得到回应,只为了告诉其他计算机自己的 IP 地址和 MAC 地址。可用于检测 IP 地址冲突。当一台主机发送了免费 ARP 请求报文后,如果收到了 ARP 响应报文,则说明网络内已经存在使用该 IP 地址的主机。可用于更新其他主机的 ARP 缓存表。如果该主机更换了网卡,而其他主机的 ARP 缓存表仍然保留着原来的 MAC 地址。这时,可以发送免费的 ARP 数据包。其他主机收到该数据包后,将更新 ARP 缓存表,将原来的 MAC 地址替换为新的 MAC 地址。

主机可以构造虚假的免费ARP应答,将ARP的源MAC地址设为错误的MAC地址,并把这个虚假的免费ARP应答发送到网络中,那么所有接收到这个免费ARP应答的主机都会更新本地ARP表项中相应IP地址对应的MAC地址。更新成功后,这些主机的数据报文就会被转发到错误的MAC地址,从而实现了ARP欺骗的攻击。

关于C/C++ Linux后端开发网络底层原理知识 后台私信【架构】 获取,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。

1.3.4 代理ARP

代理ARP就是通过使用一个主机(通常为router),来使用自己的 MAC 地址来对另一设备的ARP请求作出应答。

ARP请求是个广播包,它询问的对象如果在同一个局域网内,就会收到应答。但是如果询问的对象不在同一个局域网该如何处理?如果主机没有配置网关地址,路由器就会使用代理ARP响应主机。

两台主机A和B处于同一网段但不同的广播段(不在同一物理网络上)时,主机A发送ARP请求主机B的MAC地址时,因为路由器不转发广播包的原因,ARP请求只能到达路由器。如果路由器启用了代理ARP功能,并知道主机B属于它连接的网络,那么路由器就用自己接口的MAC地址代替主机B的MAC地址来对主机A进行ARP应答。主机A接收ARP应答,但并不知道代理ARP的存在。

代理ARP的优缺点

优点:代理ARP能在不影响路由表的情况下添加一个新的网络,使子网对该主机变得透明化。一般代理ARP应该使用在主机没有配置默认网关或没有任何路由的网络上。缺点:从工作工程可以看到,这其实是一种ARP欺骗。而且,通过两个物理网络之间的路由器的代理ARP功能其实互相隐藏了物理网络,这导致无法对网络拓扑进行网络概括。此外,代理ARP增加了使用它的那段网络的ARP流量,主机需要更大的ARP缓存空间。1.4 icmp协议

控制报文协议(Internet Control Message Protocol,ICMP)是 TCP/IP 协议族的一个子协议。ICMP 协议用于在 IP 主机和路由器之间传递控制消息,描述网络是否通畅、主机是否可达、路由器是否可用等网络状态。

icmp协议的作用数据包在发送到目标主机的过程中,通常会经过一个或多个路由器。而数据包在通过这些路由进行传输时,可能会遇到各种问题,导致数据包无法发送到目标主机上。为了了解数据包在传输的过程中在哪个环节出现了问题,就需要用到 ICMP 协议。它可以跟踪消息,把问题反馈给源主机。icmp消息的报文格式ICMP 报文一般为 8 个字节,包括类型、代码、校验和扩展内容字段。

ICMP 报文的种类有两种,即 ICMP 差错报告报文和 ICMP 询问报文。

ICMP 报文的前 4 个字节是统一的格式,共有三个字段:即类型、代码和检验和。接着的 4个字节的内容与 ICMP 的类型有关。

icmp类型和代码的意义

在 ICMP 报文中,如果类型和代码不同,ICMP 数据包报告的消息含义也会不同。常见的类型和代码的 ICMP 含义如表所示:

其中最常见的是type=0和type=8的类型

type=0:这个是ICMP的ping回复包

type=8:这个是ICMP的ping的请求包

即本机向外ping时,从本机发出的是type=8的包,收到的回应是type=0的包;

其他主机ping本机时,收到的包时type=8的包,本机回复的是type=0的包;

允许本机向外ping时,需要允许type=8的包出栈,type=0的包进栈;

禁止外部主机ping本机时,需要限制type=8的包入栈,或type=0的包出栈即可;

1.5 TCP协议1.5.1 TCP报文首部格式

TCP首部有20个固定字节,后面还有长度可变的选项部分。

TCP头部的各个字段意义如下:

源端口和目的端口

端口号用于识别应用程序,端口号的可用范围是0-65535,其中客户端的端口号一般是随机的,服务端的端口号一般是固定的。

文件 /etc/services 可查看常见软件用的端口号。

在centos7上客户端的使用的端口号范围放在/proc/sys/net/ipv4/ip_local_port_range文件中。

[root@xuzhichao ~]# cat /proc/sys/net/ipv4/ip_local_port_range 32768 60999
序号

seq号占用4个字节,表示本报文段所发送数据的第一个字节的编号。在TCP链接中所传送的字节流的每个字节都会按顺序编号。由于序列号由32位表示,所以每2^32个字节,就会出现序列号回绕,再次从0开始。

例如第一次要发送的数据分包序列号seq=1,发送的数据长度len=10,则下一次发送的分包序列号将是seq=11,以此类推。

为了确保数据通信的有序性,避免网络中乱序的问题。接收端根据这个编号进行确认,保证分割的数据段在原始数据包的位置。

确认号

确认号ack也占用4个字节,表示等待发送方下次发送数据包的起始字节,即接收端期望收到的下一个序列号。

接收方在收到一个或多个分包之后,将会给发送方回复一个确认ack包,用于告知发送方下一个要发送的包从此序号开始发送。例如接收方收到了前500个字节,此时回复ack包,确认号ack=501,表明收到了前500个字节的包,希望下一次发送方发送第501字节的包。

数据偏移

表示TCP报文段的首部长度,共4位。由于TCP首部包含一个长度可变的选项部分,需要指定这个TCP报文段到底有多长。

它指出TCP报文段的数据起始处距离TCP首部字段的起始处有多远。该字段的单位是32位(即4个字节为计算单位),4位二进制最大表示15,所以数据偏移也就是TCP首部最大60字节,可选部分最大为40字节。

URG位

紧急指针位,表示本报文段中发送的数据是否包含紧急数据。URG=1时表示该数据为紧急数据,需要尽快发送,而不是按照编号顺序传输。

ACK位

表示前面确认号字段是否有效。只有当ACK=1时,前面的确认号字段才有效。TCP规定,连接建立后,ACK必须为1,带ACK标志的TCP报文段称为确认报文段。

PSH位

接收方收到数据后先存放到Recv Buffer中,过一段时间才会从buffer中读取数据,根据包的序列号进行排序组合。如果发送方想让接收方尽快从recv buffer中接收数据,则可以发送PSH=1的包过去,这样接收方就会立即从buffer中读取数据推送给上层应用。PSH在实际中较少使用。

RST位

重置位,当RST=1时,表明需要重新建立TCP连接,会断开当前连接。出现RST=1可能是因为连接出现了差错导致。

SYN位

SYN=1,说明这是一个请求建立链接或同意建立链接的报文。只有在前两次握手中SYN才置为1。带SYN标志的TCP报文段称为同步报文段,用来建立 TCP 的连接。

SYN 标志位和 ACK 标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1。

这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有 SYN 的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口。

FIN位

如果FIN=1,即告诉对方:“我的数据已经发送完毕,你可以释放连接了”,带FIN标志的TCP报文段称为结束报文段。发送FIN标志位的 TCP 数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。

窗口

占用2个字节,最大值为65535,表示现在允许对方发送的数据量,也就是告诉对方,从本报文段的确认号开始允许对方发送的数据量。

窗口字段主要作用是当接收方的recv buffer存放不下太多的数据时或接收方处理速度变慢时,通过窗口字段要求发送方减少数据的发送量。TCP支持滑动窗口机制。

校验和

占用2个字节,该字段会对TCP首部和TCP数据部分做校验。

紧急指针

2个字节,只有URG=1时,紧急指针才会生效,该字段指出了紧急数据在数据字段中的位置。

可选项部分

其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,选项部分最长为:(2^4-1)*4-20=40字节

比较常用的字段如下:

最大报文段长度:Maxium Segment Size,MSS

指明自己期望对方发送TCP报文段时那个数据字段的长度。比如1460字节。数据字段的长度加上TCP首部的长度才等于整个TCP报文段的长度。MSS不宜设的太大也不宜设的太小。若选择太小,极端情况下,TCP报文段只含有1字节数据,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,网络的利用率就不会超过1/41。若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的TCP报文段。当传输出错时还要进行重传,这些也都会使开销增大。因此MSS应尽可能大,只要在IP层传输时不需要再分片就行。在链接建立过程中,双方都要把自己能够支持的MSS写入这一字段。MSS只出现在SYN报文中。

MTU和MSS值的关系:MTU=MSS+IP Header+TCP Header

通信双方最终协商出来的MSS值=较小MTU-IP Header-TCP Header

时间戳(Timestamps)

可以用来计算RTT(往返时间)。发送方发送TCP报文时,把当前的时间值放入时间戳字段,接收方收到后发送确认报文时,把这个时间戳字段的值复制到确认报文中,当发送方收到确认报文后即可计算出RTT。

也可以用来区分相同序列号的不同报文。因为序列号用32位表示,每2^32个序列号就会产生回绕,那么使用时间戳字段就很容易区分相同序列号的不同报文。

1.5.2 TCP的特性

TCP协议主要用如下特性:

TCP 是面向连接的。每次传输开始前要进行三次握手,传输结束后要进行四次挥手。TCP是端到端的协议。每一条 TCP 连接只能有点到点的。TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复、并且按序到达。TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接收缓存,用来临时存放双方通信的数据。TCP是面向字节流的。TCP 中的“流”(Stream)指的是流入进程或从进程流出的字节序列。“面向字节流”的含义是:虽然应用程序和 TCP 的交互是一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。TCP具有拥塞控制机制。即慢启动和拥塞避免算法。1.5.3 TCP的可靠传输

TCP协议是可靠的传输协议,它保证数据无差错,不丢失,不重复,按需到达。

TCP的可靠传输主要依赖以下机制:

数据包校验。目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这时 TCP 发送数据端超时后会重传该数据。此功能依靠TCP头部的校验和字段实现。对失序数据包重排序。TCP 将对失序数据进行重新排序,然后才交给应用层。丢弃重复数据。对于重复数据,能够丢弃重复数据。传输确认机制。当 TCP 收到对端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟一段时间。超时重发机制。当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。流量控制:TCP 连接的每一方都有固定大小的缓冲空间。TCP 的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP 使用的流量控制协议是可变大小的滑动窗口协议。1.5.3.1 TCP的传输确认

TCP协议在传输数据时,发送方发送的每一个报文都需要获得接收方的确认(ACK报文)后才认为数据发送成功。但是接收方通常不是每一个数据包都会发送ACK报文,而是对收到的多个报文一起进行确认,这样可以节省设备资源。

TCP的传输确认机制是使用TCP头部的序号和确认号字段来进行的,TCP会对发送的数据作为数据流,对每一个字节的数据从1开始进行编号,即为序号seq,然后根据接收方的窗口大小一次性发送一定字节的数据,接收方在收到多个数据后会对收到的数据进行统计确认,确认的方式就是回复报文中的确认号ack,ack值表示希望发送方下一次发送的字节编号seq,同时表明之前的字节都已经正确接收。

下图即为TCP的传输确认过程:

1.5.3.2 TCP的丢失重传

当网络中出现异常导致TCP中的部分数据包出现了丢失或存在差错,则这些数据包就需要进行重传,重传的机制如下:

TCP在传输报文时通常是一次性传输多个包,然后保留这些包,以便出现问题时重传;发送完这些包后暂停发送,等待接收对方的确认包;维护一个重传定时器,该定时器在TCP报文段第一次被发送时启动。如果超时时间内未收到接收方的应答,TCP模块将重传TCP报文段并重置定时器。接收方收到包都正常时会恢复确认包,发送方继续发送后续的包;如果接收方发现这些包中的某个包存在问题或未收到,例如第3个包,则发送确认,确认号是第3个包的起始字节序号,这样后面的包就都需要重传;发送方根据收到的确认号知道要重传第3个包之后的包,于是进行重传,接收方正常收到后,则给出确认包。

在有些情况下TCP也可以只重传那个丢失或有差错的包,而不用重传其之后的包。

linux中与TCP超时重传相关的两个内核参数:

指定TCP最少执行的重传次数,默认值是3,/proc/sys/net/ipv4/tcp_retries1指定连接放弃前TCP最多可以执行的重传次数,默认值15(一般对应13~30min),/proc/sys/net/ipv4/tcp_retries2

[root@xuzhichao ~]# cat /proc/sys/net/ipv4/tcp_retries13[root@xuzhichao ~]# cat /proc/sys/net/ipv4/tcp_retries215
1.5.3.3 TCP滑动窗口

TCP建立连接时,各端会分配一个缓冲区用来存储接收的数据,并将缓冲区的大小发送给对端。接收方发送的确认消息中包含了自己剩余的缓冲区大小。剩余缓冲区空间叫做窗口。

TCP 中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为 0 时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个 1 字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。

TCP滑动窗口机制示例图如下:

1.5.4 TCP的三次握手

三次握手的过程如下图:

三次握手过程

最初客户端和服务端都处于 CLOSED(关闭) 状态。本例中 A(Client) 主动打开连接,B(Server) 被动打开连接。一开始,B 的 TCP 服务器进程首先创建传输控制块TCB,准备接受客户端进程的连接请求。然后服务端进程就处于 LISTEN(监听) 状态,等待客户端的连接请求。

第一次握手:A 的 TCP 客户端进程也是首先创建传输控制块 TCB。然后,在打算建立 TCP 连接时,向 B 发出连接请求报文段,这时首部中的同步位 SYN=1,同时选择一个初始序号 seq = x。TCP 规定,SYN 报文段(即 SYN = 1 的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP 客户进程进入 SYN-SENT(同步已发送)状态。

第二次握手:B 收到连接请求报文后,如果同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 位和 ACK 位都置 1,确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时 TCP 服务端进程进入 SYN-RCVD(同步收到)状态。

第三次握手:TCP 客户进程收到 B 的确认后,还要向 B 给出确认。确认报文段的 ACK 置 1,确认号 ack = y + 1,而自己的序号 seq = x + 1。这时 ACK 报文段可以携带数据。但如果不携带数据则不消耗序号,这种情况下,下一个数据报文段的序号仍是 seq = x + 1。这时,TCP 连接已经建立,A 进入 ESTABLISHED(已建立连接)状态。

Q:为什么两次握手不可以呢?

A:为了防止已经失效的连接请求报文段突然又传送到了 B,从而产生错误。比如下面这种情况:A 发出的第一个连接请求报文段并没有丢失,而是在网路结点长时间滞留了,以致于延误到连接释放以后的某个时间段才到达 B。本来这是一个早已失效的报文段。但是 B 收到此失效的链接请求报文段后,就误认为 A 又发出一次新的连接请求。于是就向 A 发出确认报文段,同意建立连接。对于上面这种情况,如果不进行第三次握手,B 发出确认后就认为新的传输连接已经建立了,并一直等待 A 发来数据。B 的许多资源就这样白白浪费了。如果采用了三次握手,由于 A 实际上并没有发出建立连接请求,所以不会理睬 B 的确认,也不会向 B 发送数据。B 由于收不到确认,就知道 A 并没有要求建立连接。

Q:为什么不需要四次握手?

A:有人可能会说 A 发出第三次握手的信息后在没有接收到 B 的回应就已经进入了连接状态,那如果 A 的这个确认包丢失或者滞留了怎么办?我们需要明白一点,完全可靠的通信协议是不存在的。在经过三次握手之后,客户端和服务端已经可以确认了双方的通信状态是正常的,都收到了确认信息。所以即便再增加握手次数也不能保证后面的通信完全可靠,所以是没有必要的。

Q:传了 SYN,为什么还要传 ACK?

A:双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。

1.5.5 TCP的四次挥手

TCP的四次挥手过程如下:

TCP四次挥手过程

1.处于连接状态的双方,其中一方比如(A)没有数据传输了,向B发出一个FIN=1,seq=u的数据包,状态由ESTABLISHED切换到FIN-WAIT-1(终止等待1)状态

2.B接收到数据包后向A发送确认信息包ACK=1,seq=v,ack=u+1,同时状态由ESTABLISHED切换到CLOSE-WAIT(关闭等待)

3.A收到B的确认包后,转态由FIN-WAIT-1(终止等待1)切换到FIN-WAIT-2(终止等待2),这是已经为半关闭状态,同时B可能仍然有数据没有传输完,B继续把剩余数据传送完后,再向A发送结束请求FIN=1,ACK=1,seq=w,ack=u+1同时B进入LAST-ACK(最后确认)状态

4.A收到结束请求后回应ACK=1,seq=u+1,ack=w+1并且进入TIME-WAIT(时间等待),等待2倍的MSL时间,MSL是A与B之间发送数据包的时间。主要是为了避免B向A传输的剩余数据滞后于关闭请求到达,造成数据丢失。

5.B收到结束回应,就会进入CLOSED状态。A在时间等待后,也进入CLOSED状态。注意:由于TCP是全双工的,因此在每一个方向都必须单独关闭。当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这个方向上没有数据传输,一个TCP连接在接收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

Q:为什么 TIME-WAIT 状态必须等待 2MSL 的时间呢?

A:

为了保证 A 发送的最后一个 ACK 报文段能够到达 B。如果这个 ACK 报文段丢失,而使处在 LAST-ACK 状态的 B 收不到对已发送的 FIN + ACK 报文段的确认。B 会超时重传这个 FIN+ACK 报文段,而 A 就能在 2MSL 时间内(超时 + 1MSL 传输)收到这个重传的 FIN+ACK 报文段。接着 A 重传一次确认,重新启动 2MSL 计时器。最后,A 和 B 都正常进入到 CLOSED 状态。如果 A 在 TIME-WAIT 状态不等待一段时间,而是在发送完 ACK 报文段后立即释放连接,那么就无法收到 B 重传的 FIN + ACK 报文段,因而也不会再发送一次确认报文段,这样,B 就无法按照正常步骤进入 CLOSED 状态。

防止已失效的连接请求报文段出现在本连接中。A 在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。

Q:为什么第二次跟第三次不能合并, 第二次和第三次之间的等待是什么?

A:当服务器执行第二次挥手之后, 此时证明客户端不会再向服务端请求任何数据, 但是服务端可能还正在给客户端发送数据(可能是客户端上一次请求的资源还没有发送完毕),所以此时服务端会等待把之前未传输完的数据传输完毕之后再发送关闭请求。

保活计时器的作用

除时间等待计时器外,TCP 还有一个保活计时器(keepalive timer)。设想这样的场景:客户已主动与服务器建立了 TCP 连接。但后来客户端的主机突然发生故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用保活计时器了。服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两个小时。若两个小时都没有收到客户端的数据,服务端就发送一个探测报文段,以后则每隔 75 秒钟发送一次。若连续发送 10个 探测报文段后仍然无客户端的响应,服务端就认为客户端出了故障,接着就关闭这个连接。

孤儿连接

处于FIN_WAIT_2状态的客户端需要等待服务器发送结束报文段,才能转移至TIME_WAIT转态,否则它将一直停留在这个状态。如果不是为了在半关闭状态下继续接收数据,连接长时间地停留在FIN_WAIT_2状态并无益处。连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接由内核来接管,可称之为孤儿链接(和孤儿进程类似)。

Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核参数

指定内核能接管的孤儿连接数目/proc/sys/net/ipv4/tcp_max_orphans

指定孤儿连接在内核中生存的时间/proc/sys/net/ipv4/tcp_fin_timeout

[root@xuzhichao ~]# cat /proc/sys/net/ipv4/tcp_max_orphans4096[root@xuzhichao ~]# cat /proc/sys/net/ipv4/tcp_fin_timeout60
1.5.6 TCP拥塞控制

拥塞控制和流量控制不同,前者是一个全局性的过程,而后者指点对点通信量的控制。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致于过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

慢开始:

发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态的变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接收方的接收能力,发送窗口可能小于拥塞窗口。思路就是:不要一开始就发送大量的数据,先试探一下网络的拥塞程度,也就是说由小到大增加拥塞窗口的大小。

拥塞避免算法

让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍,这样拥塞窗口按照线性规律缓慢增长。无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为⽆法判定,所以都当作拥塞处理),就把慢开始门限设置为出现拥塞时的发送窗口的一半,然后把拥塞窗口设置为1,执行慢开始算法:

1.5.7 SYN攻击

客户端只发SYN,伪造源地址,发出后,目标地址会发回应包,由于地址时伪造的不存在,会等待对方回应,在TCP超时重传时间内,sync queue半连接队列记录是不会删除的,而半连接的数目是有上限的,如果构建很多这种假地址,就会将连接数填满,造成正常的用户请求无法得到回应。是一种常见的DOS攻击。

判断是否受到SYN攻击的方式为使用netstat命令或ss命令查看网络连接状态,如果有大量的SYN_RECV状态而且源地址都是乱七八糟的,则一般说明受到了SYN攻击。

使用netstat命令或ss命令查看方法如下:

[root@xuzhichao ~]# netstat -tnlpa | grep tcp | awk '{print $6}' | sort | uniq -c[root@xuzhichao ~]# ss -o state syn-recv | wc -l
1.6 UDP协议UDP协议的报文格式:UDP协议的特点:

UDP 是无连接的;

UDP 使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态;

UDP 是面向报文的;

UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 直播,实时视频会议等);

UDP 支持一对一、一对多、多对一和多对多的交互通信;UDP 的首部开销小,只有 8 个字节,比 TCP 的 20 个字节的首部要短。

TCP和UDP的主要区分

TCP提供面向连接的传输,通信前要先建立连接(三次握手机制);UDP提供无连接的传输,通信前不需要建立连接。

TCP提供可靠的传输(有序,无差错,不丢失,不重复);UDP提供不可靠的传输。

TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组;UDP是面向数据包的传输,没有分组开销。

TCP提供拥塞控制和流量控制机制;UDP不提供拥塞控制和流量控制机制。

标签: #tcpip网络编程原理与技术