计算机网络要实现通信就必须约定好一个通信的标准,以保证通信双方知道由谁来发起通信,怎么通信,通信使用哪一种语言,怎样结束通信等,这一切都需要事先规定好,而为此制定的规则就称为协议。
首先TCP/IP协议看上去好像就是TCP协议和IP协议,实际上不是的。
TCP/IP协议(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)叫网路通信协议,是指能够在多个不同网络间实现信息传输的协议簇。
TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇,包括了TCP/IP四层模型(OSI七层模型和TCP/IP五层(或四层)模型)中的所有协议, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。
TCP协议与UDP协议都是传输层协议,不过二者具有不同的特性,同时应用场景也不同。
首先先总结一下TCP与UDP的特点及区别。
特性 | TCP | UDP |
---|---|---|
可靠性 | 可靠 | 不可靠 |
连接性 | 面向连接 | 无连接 |
数据单位 | 面向字节流 | 面向数据报 |
传输效率 | 低 | 高 |
流量控制 | 滑动窗口 | 无 |
拥塞控制 | 慢开始、拥塞避免、快重传、快恢复 | 无 |
传输方式 | 全双工 | 一对一、一对多、多对一、多对多 |
应用场景 | 效率要求相对低,准确性要求高 | 效率要求高,准确性要求相对低 |
接下来我们就根据对TCP和UDP的详细介绍来理解它们之间的不同。
UDP是Internet 协议集中一个无连接,面向数据报的协议, 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法
面向数据报: 应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并。
UDP数据报分为首部和用户数据部分
16位源端口号,目的端口号: 端口号0-65535,表示数据从哪个进程来,到哪个进程去。
16位UDP长度: 表示整个数据报(UDP首部+UDP数据)的最大长度
16位UDP检验和: 检测UDP数据报在传输中是否有错,有错则丢弃。
甚至UDP的源端口号和16位校验和都可以不要,直接令该字段为0就行。
1.UDP不会对数据包进行检查,它不会保证数据报分组的顺序。
2.UDP在进行消息交付时不保证交付一定可以成功。许多细微的处理它会交给上层的应用层去做。因为没有像TCP一样的确认应答机制和重传机制,所以它只管自己发出去数据报,接收端有没有收到数据报不是它的考虑范围。
3.UDP只有一个接收缓冲区, 如果缓冲区满了, 再到达的UDP数据就会被丢弃。
在复杂的网络环境中,网络拥塞,负载过高等经常发生,IP分组发送失败也是每时每刻都在发生的。UDP的这些特性就导致它的不可靠。
UDP不可靠那么为什么还需要UDP呢?
UDP主要用于对高速传输和实时性有较高要求的通信或广播通信。比如视频通话、直播这种实时的并且一小部分数据丢失对整体通信影响不大的情况。如果使用TCP在每次发生数据丢失后进行重传的话,可能会导致整个通信延迟过大,而且TCP连接的建立、维护、断开也是一笔开销。
TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制。经常说TCP是面向连接的,可靠的流协议,那么面向连接、可靠、面向字节流怎么理解呢?
面向连接: 在应用TCP协议进行通信之前双方通常需要通过连接管理机制来建立TCP连接,连接建立后才能进行正常的数据传输。
可靠性: 可靠性主要体现在三个方面:
因此可靠性需要TCP协议具有超时与重传管理、窗口管理、流量控制、拥塞控制等功能。
面向字节流: 应用层发送的数据会在TCP的发送端缓存起来,如果发送的字节数太长, 会被拆分成多个TCP的数据包发出。如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去。
TCP首部和数据部分封装在一起,携带在IP数据报的数据部分。
16位源端口、目的端口: 表示数据从哪个进程来,到哪个进程去。
TCP的源端口、目的端口、以及IP层的源IP地址、目的IP地址、传输层协议也就是我们常说的五元组唯一的标识了一个TCP连接。
32位序列号: 标识了TCP报文中在对应方向的传输中第一个byte对应的字节序号。比如发送一个包大小为1000byte(1~1000),序列号seq=1,下一个包的seq=1+1000=1001。通过系列号,TCP接收端可以识别出重复接收到的TCP包,从而丢弃重复包,同时对于乱序数据包也可以依靠系列号进行重排序,进而对高层提供有序的数据流。
32位确认号: 标识了报文发送端期望接收的字节序列。如果设置了ACK控制位,这个值表示一个准备接收的包的序列码,注意是准备接收的包。比如接收了一个包大小为1000byte(1~1000),seq=1,那么发送端会回复一个确认收到的数据包,确认序列号ack=1+1000=1001,表示下一个我想收到的数据包。
4位首部长度: 表示该TCP头部的长度,最大为15,单位32位bit(有多少个4字节),所以TCP首部部最大长度是15 * 4 = 60。
6位标志位:
16位窗口: 指示了从Ack Number开始还愿意接收多少byte的数据量,也即用来表示当前接收端的接收窗口还有多少剩余空间。用于TCP的流量控制。
16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分。
16位紧急指针: 标识哪部分数据是紧急数据,指向后面是优先数据的字节,在URG标志设置了时才有效。
TCP将每个字节的数据都进行了编号. 即为序列号。
每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B。
如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发。
又或者是B在收到了A发来的数据包之后,在给A回应的过程中丢包了,这时A没有收到应答也会重发。等重发过后B这边又收到再回应,不过在这过程中B收到了两个重复的包,那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉.,这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果。
也就是常说的三次握手建立连接,四次挥手断开连接。
三次握手
第一次握手: 客户端发送SYN报文包请求连接,SYN标记位设置为1,请求序列号seq设置为x,客户端进入SYN_SENT状态。(我要与你建立连接了)
第二次握手: 服务端收到客户端的SYN包,并确认SYN标记后,发送确认包ACK,设置确认标记ACK=1,确认序列号ack=x+1(SYN会占用一个字符)。同时自己也要发送一个SYN报文段,设置SYN=1,seq=y。此时服务器进入 SYN_RECV状态。(好的,我收到你与我建立连接的请求了,我同意并发送一个SYN包来与你建立连接)
第三次握手: 客户端收到服务端的SYN+ACK报文段,向服务端发送ACK确认包,设置确认标记ACK=1,确认序列号ack=y+1。此包发送完毕客户端和服务端都进入ESTABLISHED状态,完成TCP 的三次握手。(ok,我收到你也同意并与我建立连接的消息了,连接建立完成)
服务端状态转化 :
客户端状态转化:
四次挥手
当客户端和服务端通过三次握手建立了 TCP 连接以后,当数据传送完毕,断开连接就需要进行TCP的四次挥手。
第一次挥手: 客户端首先调用close执行“主动关闭”,向服务器发送一个 FIN包,FIN=1,请求序列号seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1)。客户端进入 FIN_WAIT_1 状态,表示客户端没有数据要发送给服务端了。(我没有数据要发了,我要与你断开连接)
第二次挥手: 服务器端收到FIN包,向客户端发送确认包ACK,ACK=1,表示收到了你要断开连接的请求,确认序列号ack=u+1,并带上自己的请求序列号seq=v。此时,客户端就会进入FIN_WAIT_2状态,服务端进入CLOSE-WAIT(关闭等待)状态,也就是“半关闭”状态,因为此时的客户端已经没有数据要发了,但服务端可能还有数据要发给客户端,发送的数据客户端依然要接收。(你要与我断开连接,我收到了,但是我还有数据没发完,暂时还不能断开)
第三次挥手: 服务器数据发完之后,向客户端发送FIN包,FIN=1,ACK=1,这里的ACK不是确认收到报文段而是表示准备好释放连接了,序列号seq=w,确认序列号ack=u+1,服务器就进入了LAST-ACK状态,等待客户端的确认。(我数据发送完了,可以与你断开连接了,发送FIN包与你断开连接)
第四次挥手: 客户端接收到服务端的FIN包后,发送确认包ACK,ACK=1,表示“接收到服务器准备好释放连接的信号”.请求序列号seq=u+1,确认序列号ack=w+1,客户端就进入了TIME-WAIT状态,此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。(好的我收到你也同意断开连接的请求了,进入TIME-WAIT状态)。
服务端状态转化:
客户端状态转化:
无论是客户还是服务器,任何一端都可以执行主动关闭。一般情况都是客户端先进行关闭。
客户端为什么在TIME_WAIT状态要等待2MSL的时间?
MSL是TCP报文在网络中的最大生存时间, 因此TIME_WAIT持续存在2MSL的话有两个作用
刚才我们讨论了确认应答策略, 对每一个发送的数据段, 都要给一个ACK确认应答. 收到ACK后再发送下一个数据段. 这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候。
既然这样一发一收的方式性能较低, 那么我们一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。
那么如果出现了丢包,如何进行重传?这里分两种情况讨论
引入:
接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应。
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)。
流量控制:
引入:
虽然TCP有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题。
因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据, 是很有可能引起雪上加霜的。
TCP进行拥塞控制的四种算法:慢开始、拥塞避免、快速重传、快速恢复。此处引入一个概念程为拥塞窗口。
拥塞窗口:
“拥塞窗口”就是“拥塞避免”的窗口,它是一个装在发送端的可滑动窗口,窗口的大小是不超过接收端确认通知的窗口。
rwnd: 接收窗口大小
cwnd:拥塞窗口大小
实际发送窗口的大小 = min(rwnd, cwnd)
拥塞控制:
1.慢启动算法
TCP引入慢启动机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据
但像这样的窗口增长是指数级的,为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍,此处引入一个叫做慢启动的阈值(ssthresh),当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长进入拥塞避免阶段。
2.拥塞避免算法
RTT(Round-Trip Time): 从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认,不包含数据传输时间)总共经历的时间.
拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT总共经历的时间)就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口按线性规律缓慢增长,如果发生网络拥塞,就将慢启动门限设为原来的一半,然后将拥塞窗口设置为1,重新开始执行慢启动算法。如下图:
3.快速重传算法
快重传算法要求首先接收方收到一个失序的报文段后就立刻发出重复确认,而不要等待自己发送数据时才进行捎带确认。当发送方接收到3个以上的重复ACK,TCP就认为发生了网络拥塞(有报文丢失),需要重传。此时不需要等到重传定时器超时,所以称之为快速重传。
4.快速恢复
将慢启动阈值设置为原来的一半,然后将拥塞窗口设置为现在的慢启动阈值,不再执行慢启动而是直接进入拥塞避免阶段。使发送窗口成线性方式增长。
少量的丢包, 我们仅仅是触发超时重传;。大量的丢包我们就认为网络拥塞。
当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降。
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案。
4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4。IPv6为6。
4位首部长度: 表示该IP头部的长度,最大为15,单位32位bit(有多少个4字节),所以IP首部部最大长度是15 * 4 = 60。
8位区分服务: 3位优先权字段(已经弃用), 4位TOS字段, 和1位保留字段(必须置为0). 4位 TOS分别表示: 最小延时, 最大吞吐量, 最高可靠性, 最小成本. 这四者相互冲突, 只能选择一个. 对于ssh/telnet这样的应用程序, 最小延时比较重要; 对于ftp这样的程序, 最大吞吐量比较重要。
16位总长度: 整个IP数据报的最大长度。
16位标识(id): 唯一的标识主机发送的报文. 如果IP报文在数据链路层被分片了, 那么每一个分片里的这个id都是相同的。
3位标志字段: 第一位保留位,未使用。第二位置为1表示禁止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文. 第三位置1,表示后边还有分片, 为0表示后边没有分片了,类似于一个结束标记。
13位分片偏移: 是分片相对于原始IP报文开始处的偏移. 其实就是在表示当前分片在原报文中处在哪个位置. 实际偏移的字节数是这个值 * 8 得到的. 因此, 除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了)。
8位生存时间(TTL): 数据报到达目的地的最大报文跳数. 一般是64. 每次经过一个路由器, 该TTL减1一直减到0还没到达, 那么就丢弃了.。这个字段主要是用来防止出现路由循环。
8位协议: 表示上层协议的类型。
16位首部校验和: 检验IP头是否出错,如果出错则丢弃。
32位源地址与32位目的地址: 表示发送端和接收端。
可选择段: 一般不考虑。
1.IP地址概述
IP 地址(IPv4 地址)由32位正整数来表示,分为两个部分网络号和主机号。
网络号: 网络标识必须保证相互连接的两个网段具有不同的标识。
主机号: 同一网段内, 主机之间具有相同的网络号, 但是必须有不同的主机号。
如图所示:
2.IP地址分类
IP地址分为5类:
注意: 在分配IP地址的时候,用主机地址不可以全部为 ”0“ 或全部为 “1”。
3.子网掩码
随着Internet的飞速发展,这种划分方案的局限性很快显现出来,大多数组织都申请B类网络地址, 导致B类地址很快就分配完了, 而A类却浪费了大量地址;
针对这种情况提出了新的划分方案, 称为CIDR(Classless Interdomain Routing):
如下两个划分子网的例子:
IP地址与子网掩码做与运算可以得到网络号, 主机号从全0到全1就是子网的地址范围。
IP地址 | 子网掩码 | 网络号 | 子网地址范围 |
---|---|---|---|
152.168.40.68 | 255.255.255.0 | 152.168.40.0 | 152.168.40.0~152.168.40.255 |
152.168.40.68 | 255.255.255.240 | 152.168.40.64 | 152.168.40.64~152.168.40.79 |
子网掩码的另一种表示方式,在每个 IP 地址后面追加网络地址的位数用 “/ ” 隔开,如:152.168.40.68/24 表示IP地址为152.168.40.68,子网掩码的高24位为1,二进制表示为11111111 11111111 11111111 00000000也就是子网掩码为255.255.255.0。
4.私有IP地址和公有IP地址
如果一个组织内部组建局域网,IP地址只用于局域网内的通信,而不直接连到Internet上,理论上 使用任意的IP地址都可以,但是RFC 1918规定了用于组建局域网的私有IP地址。
私有地址范围:
一个路由器可以配置两个IP地址, 一个是WAN口IP, 一个是LAN口IP(子网IP).
路由器LAN口连接的主机, 都从属于当前这个路由器的子网中。
不同的路由器, 子网IP其实都是一样的(通常都是192.168.1.1). 子网内的主机IP地址不能重复. 但是子网之间的IP地址就可以重复了。
每一个家用路由器, 其实又作为运营商路由器的子网中的一个节点. 这样的运营商路由器可能会有很多级,最外层的运营商路由器, WAN口IP就是一个公网IP了。
子网内的主机需要和外网进行通信时, 路由器将IP首部中的IP地址进行替换(替换成WAN口IP), 这样逐级替换, 最终数据包中的IP地址成为一个公网IP. 这种技术称为NAT(Network Address Translation,网络地址转换)。
如果希望我们自己实现的服务器程序, 能够在公网上被访问到, 就需要把程序部署在一台具有外网IP的服务器上. 这样的服务器可以在阿里云/腾讯云上进行购买。
1.概述
在复杂的网络结构中, 找出一条通往终点的路线。
互联网是由许多个网络连接所形成的大型网络。如果要在互联网中传送IP信息包,除了确保网络上每个设备都有一个唯一的IP地址之外,网络之间还必须有传送的机制,才能将IP信息包通过一个个的网络传送到目的地。此种传送机制称为IP路由。
2.路由控制
各个网络通过路由器相连,换句话说需要通过路由器的合理配合,才能将IP包送到目的地。
那么路由器怎么确定当前这个数据包该送到哪里呢?
这就要依靠存储在路由器中的路由表来决定了。
3.路由表
在计算机网络中,路由表(routing table)或称路由择域信息库(RIB, Routing Information Base),是一个存储在路由器或者联网计算机中的电子表格(文件)或类数据库。路由表存储着指向特定网络地址的路径。路由表中含有网络周边的拓扑信息。路由表建立的主要目标是为了实现路由协议和路由选择。
路由表的形成方式有两种:
五、IP分包与组包