当前的网络协议是分层的:应用层,传输层,网络层,数据链路层,物理层
目录
应用层
传输层
UDP协议
数据报格式
TCP(传输控制协议)
报文格式
1.确认应答(核心)
2.超时重传
3.连接管理
4.滑动窗口
5.流量控制
6.拥塞控制
7.延时应答
8.捎带应答(延时应答的基础上)
9.面向字节流
10.TCP中的一些异常情况
网络层
IP协议
路由选择
数据链路层
核心的协议:以太网
认识MTU
MTU对IP协议的影响
MTU对UDP协议的影响
MTU对于TCP协议的影响
ARP协议
物理层
总结
应用层
传输层
网络层
数据链路层
应用层是程序员打交道最多的一个阶层,和程序相关,其余的就是和操作系统内核相关的以及硬件设备相关的层面。
在应用层的协议分为大致两种:
1.使用现成的协议如http,ftp,ssh等
比如:在浏览器输入一个地址,打开一个网址,外卖下单,在这个网络通信中使用的就是http协议
2.自定义协议:客户端和服务器程序员自己开发,进行一系列的编程和代码实现
自定义协议在自己定义时候需要注意的问题
1.约定请求和响应是什么格式的?
2.客户端和服务器是如何交互的,具体体现为客户端给服务器的请求是啥样的,服务器给客户端的响应是啥样的。
自定义类型大致分为两类
1.文本格式(请求响应当成字符串处理,处理的基本单位是字符)
2.二进制格式(请求响应当成二进制处理,处理的基本单位是字节)
自定义协议只要约定请求和响应的格式即可。
负责端口对端口的数据传输,只考虑 起点和终点
特点
1.无连接:知道对端的IP和端口号就直接进行传输, 不需要建立连接
2.不可靠:没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返 回任何错误信息
3.面向数据报:不能够灵活的控制读写数据的次数和数量
4.全双工:既可以读也可以写
源端口号:发送的起点
目的端口:发送的终点
报文长度:表示当前的udp数据报的总长度。16个bit位,代表的最大长度为65535(2的16次方-1),单位为字节,因此udp最大报文长度为16k
校验和:为了验证当前的数据是否出现问题,如果校验和不正确,数据一定不正确。
网络传输的本质都是O/I的bit流,这些bit流都是通过光信号或者电信号表示的,如果遇到干扰可能会出现1→0,0→1的情况导致数据传输出现问题(如极端情况:太阳风暴。。。)
常用的校验和有
1.crc循环冗余校验和,原理是二进制存储,依次取出数据进行累加,传输数据时候把crc一起传输给目标,接收方收到了数据和校验和,接收方采用同样的算法进行验证,但是有一定的概率不同的数据的校验和是一样的。
2.MD5校验和,现阶段使用较多的校验和,具有
(1)定长(无论输入字符串多长得到的都是固定长度),
(2)分散(输入的字符串变化很小md5的值差距都会很大)
(3)不可逆(给定原数据很容易得到MD5的值,但是MD5的值很难回去到原来的字符串,例如:给你一个猪肉,你可以把他做成火腿肠,给你一根火腿肠,你不能把他变成一块猪肉)
的特点,由于MD5的第二个特性基本不会出现两个字符串的MD5值相同,这种概率就比crc低很多,因此MD5还可以应用在一些密码学的场合。(银行的程序员哪怕看到了用户的密码,也是MD5加密了很多次,不可能被破译)但是MD5的可逆过程也是存在的,是基于计算机的算力,当前的计算机基本无法破译。
源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
32位序号/32位确认号:tcp协议在具体传输中会根据传输能力,将文件分为若干份 ,这32位序列号就是为数据段打上标记,以便接收端得到数据后,重组数据段。如过接收端没有收到(网络丢包了)就会请求重新发送,保证数据的完整性。
4位TCP报头长度: 表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60 6位标志位:
URG: 紧急指针是否有效
ACK: 确认号是否有效
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
16位窗口大小: 后面再说
16位校验和: 发送端填充,
CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也 包含TCP数据部分.
16位紧急指针: 标识哪部分数据是紧急数据;
40字节头部选项: 暂时忽略
TCP相比UDP的区别在于可靠性,所谓可靠性就是在发送一条消息时,知道对方是否应答,(相当于已读),但是可靠性也不是tcp所专有的。
每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发
主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B; 如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发,在传输数据的时候还可能出现丢包,一般以500ms为单位计时。
问题:如果数据重复了怎么办?
接收方收到的数据会存放在缓冲区会按照序号进行去重,此时应用程序读到的数据就是不重复的。
下面将通过连接管理具体介绍三个保证可靠性的操作
三次握手
过程: 主机A(如客户端)向主机B(服务器)发送一个请求尝试建立连接,发送一个syn(同步报文段)同时b向A回复syn和ack也尝试和a建立连接,这是也是在确认a的请求,a收到以后回复ack(确认报文段)双方就可以确认连接。(具体过程就像打电话一样)
本质上应该是四次握手,但是b确认消息和回复a都是在系统内核进行的,二者是同时发生的,因此b回复的syn和ack可以合在一起返回给a。
下面补充几个常见的关于三次握手的面试题
为什么要进行三次挥手为什么要建立连接?
1.投石问路确认a和b之间的传输是通畅的,尤其是确认,A和b的接受和发送能力是否正常。
举一个例子:相当于两个人打电话两个人都要先喂,确认自己的话筒和对方的听筒正常
2.协商参数:通过这次握手,让a和b进行交流,选择一些合适的传输参数,例如tcp的序号从多少开始
三次握手必须是三次吗?两次行不行,四次行不行?
四次握手是可以的,但是可以却没必要,因为中间b返回ack和syn分成两部分进行,系统对其要进行多一次的封装和打包,两份数据报干了一个数据包的事情,开销就会大,所以没必要使用四次。
如果是两次握手那么b就会不知道a的通信是否正常。
相当于打电话
a:喂,在吗?
b:在呢(此时b知道a的话筒和自己的听筒正常)
a:(不说话)(此时b就不知道自己的话筒和a的听筒是否正常,就不能保证已经建立了连接)
四次挥手
四次挥手是用来断开与另一台机器的连接的。
过程:主机a向b发送fin(结束报文段),主机b 收到后进入close-wait状态,并发送ack(这个操作是内核完成额的),确认应答,直到用户代码实现close,向主机b发送fin(用户态,即代码实现),a收到后返回b一个ack二者断开连接。
这里补充说明一下Tcp的close-wait和time-wait状态
close-wait状态:服务器收到fin后进入的状态,等待用户代码实现close,发送fin
time-wait状态:客户端收到fin后进入的状态,这个状态的存在就是为了处理ACK丢包问题,
这个状态即使是进程已经退出了,依然存在,TCP不会立即销毁,会等待一定时间,如果短时间没有fin过来才会真正销毁。
1.比如第一个FIN丢了,a收不到ack,就会重传fin
2.第一个ack丢了,a收不到ack就会重传fin
3.第二个fin丢了,b收不到ack就会重传fin
4.第二个ack丢了,b收不到ack还是会重传fin
问题:如果a收到fin立刻返回ack,线程立刻销毁,不是进入time-wait会怎么样?
此时一旦最后一个ack丢了,此时就无法重传ack了,因为连接已经销毁。
问题:为什么服务器会出现大量的close-wait?
代码出现bug没有及时调用close函数
由于time-wait的存在还引入了绑定端口失败的情况
进程退出后,time-wait状态仍然存在,tcp连接也存在,如果服务器先退出,服务器会进入time-wait,(此时原来的连接依然占据端口),服务器如果立即启动,新的进程会尝试重新绑定这个端口。(出现绑定失败)
四次挥手问什么不能是三次?
由于回答的fin和ack不是在同一时间发生的,ack返回之后,可能过了很久用户代码才会执行close,因此二者存在一定的时间间隔,不会在同一个包里。(但是在延时应答机制和捎带应答可能会出现三次的情况)
四次挥手一定会执行吗?
不一定,如果非正常断开,比如你在打游戏,别人把你的网线剪了。。
tcp在保证可靠传输的同时也要保证效率,但是其实效率和可靠性是矛盾的,但是TCP也做了一系列的优化。
在数据传输的过程中,等待响应的是很耗费时间的,就例如你去买饭,你需要一碗紫菜汤,一碗馄饨,一笼包子,你当然不是等一个做好了,再给店家说你的下一个需求,而是一次发送很多请求,让他一起做,他就可以在蒸包子的过程中完成煮汤和煮混沌的过程。TCP也是如此,不是一个一个发送请求,而是批量发送。
滑动窗口就是批量发送数据的工具,一次发送多组数据批量发送,,多组等待时间重叠,提高效率。窗口大小固定,接受方收到一定数据后,滑动窗口会补充至串口大小数量的数据继续发送, 按道理来说,窗口越大,速度越快。
对于滑动窗口来说,窗口的大小决定了传输的效率,窗口大小和传输速率成正比,但是显然不可能是一味的无限大,还要考虑接收方的接受能力,(每个socket都在内存上有一段缓冲区,缓冲区也是有大小的,如果串口很大缓冲区增长也会很快)。因此要根据接收方的能力来控制窗口大小。
流量控制就是根据接收方缓冲区剩余的大小来动态控制窗口大小。
缓冲区的剩余大小包含在TCP的报头协议中,返回给发送端
问题:如果窗口大小为0,就不会发送了吗?
此时发送方是不会继续发送数据的,但是为了能够查询接收方窗口的大小(因为不可能时刻都为0),每隔一段时间就会发送一个窗口探测包,通过这个包,出发了ack,就知道当前的剩余缓冲区大小了。这种方式叫动态适应
流量控制是根据接收方的接受能力决定发送方的发送能力的,而在传输途中,我们还要考虑网络的通畅(类似于去一个地方还要考虑交通是否通畅,这条路能跑多少车),这就是要考虑中间链路层。因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据, 是 很有可能引起雪上加霜的.
此处引入一个概念程为拥塞窗口 发送开始的时候, 定义拥塞窗口大小为1; 每次收到一个ACK应答, 拥塞窗口加1; 每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗 口
先使用较小的窗口传输查看网络是否通畅,如果不丢包说明网络通畅,就逐渐加大速率。反之同理,真实的发送窗口大小是流量控制窗口和拥塞控制窗口中较小的那个值(木桶原理)。
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小,
假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K; 但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了; 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来; 如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M
在接收方收到数据后,进行等待一段时间,此时可能另一个数据的请求也完成了,就可以一起打包(减少了封装的次数)
这种情况下四次挥手可能变成三次,比如服务器收到请求处理数据,返回应答过程需要消耗30ms,延时应答时间为50ms,程序执行完毕为10ms,此时在延时应答的时间内完成了ack和响应就可以一起打包发送给客户端,四次挥手就变成了三次。
创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;调用write时, 数据会先写入发送缓冲区中; 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出; 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去; 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区; 然后应用程序可以调用read从接收缓冲区拿数据; 另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以 写数据. 这个概念叫做 全双工。
由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:
写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节; 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次 read一个字节, 重复100次;
粘包问题(不是tcp特有,面向字节流都有这样的问题)
首先要明确, 粘包问题中的 "包" , 是指的应用层的数据包. 在TCP的协议头中, 没有如同UDP一样的 "报文长度" 这样的字段, 但是有一个序号这样的字段. 站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中. 站在应用层的角度, 看到的只是一串连续的字节数据. 那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数 据包。
那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界
方案1:设定结束符,约定每一个字段以什么结尾,就知道了包的结束位置
方案2:设定包的长度,约定每个应用层亲四个字节存储数据报的长度,应用程序就会先读取四个字节,得到长度再根据长度读取几个字节
核心:明确边界!!!!
这就相当于是不加标点符号的一句话:面试官真就帅一点也不沾丑
理解1:面试官真就帅 ,一点也不沾丑!!
理解2:面试官,真就帅一点也不沾,丑!!!!
1.进程终止:释放对应的文件描述符,触发四次挥手,不代表连接终止,相当于调用socket.close
2.机器重启:先杀进程,仍然执行四次挥手
3.机器掉包/网络断开:机器来不及任何动作。
(1)掉电的是接收方:另一边还在发送数据,发送不会有ack就会超时重传,重传几次后就会尝试重新连接。复位报文段然后发送方就会放弃这个连接,回收对应资源。
(2)掉电的是发送方:另一边不知道是发送方挂了还是暂时没发(心跳包机制解决)
问题:啥样的场景适合TCP啥样的适合UDP?
1.如果需要可靠性首选TCP2.如果传输的单个数据报长,使用tcp
3.如果注重效率预先考虑UDP
4.如果需要广播优先考虑udp
4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4.
4位头部长度(header length): IP头部的长度是多少个32bit, 也就是 length * 4 的字节数. 4bit表示最大的 数字是15, 因此IP头部最大长度是60字节.
8位服务类型(Type Of Service): 3位优先权字段(已经弃用), 4位TOS字段, 和1位保留字段(必须置为0). 4位 TOS分别表示: 最小延时, 最大吞吐量, 最高可靠性, 最小成本. 这四者相互冲突, 只能选择一个. 对于 ssh/telnet这样的应用程序, 最小延时比较重要; 对于ftp这样的程序, 最大吞吐量比较重要.
16位总长度(total length): IP数据报整体占多少个字节.
16位标识(id): 唯一的标识主机发送的报文. 如果IP报文在数据链路层被分片了, 那么每一个片里面的这个id 都是相同的.
3位标志字段: 第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到). 第二位置为1表示禁 止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文. 第三位表示"更多分片", 如果分片了的话, 最 后一个分片置为1, 其他是0. 类似于一个结束标记.
13位分片偏移(framegament offset): 是分片相对于原始IP报文开始处的偏移. 其实就是在表示当前分片在 原报文中处在哪个位置. 实际偏移的字节数是这个值 * 8 得到的. 因此, 除了最后一个报文之外, 其他报文的 长度必须是8的整数倍(否则报文就不连续了).
8位生存时间(Time To Live, TTL): 数据报到达目的地的最大报文跳数. 一般是64. 每次经过一个路由, TTL -= 1, 一直减到0还没到达, 那么就丢弃了. 这个字段主要是用来防止出现路由循环
8位协议: 表示上层协议的类型
16位头部校验和: 使用CRC进行校验, 来鉴别头部是否损坏.
32位源地址和32位目标地址: 表示发送端和接收端. 选项字段(不定长, 最多40字节): 略
32位ip地址如果不够用怎么办?
32位ip地址大约为42亿9000万,在几十年前科学家根本想不到互联网的发展能快成这样,
IP地址数量已经远远小于计算机的数量了
1.并不是所有设备都联网了,联网才分配ip,不联网不分配(治标不治本,动态分配)
2.NAT机制,使用一个ip地址代表一批主机(小区好几个用户都使用中国移动,外网ip都一样)
3.ipv6机制,使用128位表示ip,相比v4地址大了很多很多。
知识补充:IP地址分为两个部分, 网络号和主机号 网络号: 保证相互连接的两个网段具有不同的标识; 主机号: 同一网段内, 主机之间具有相同的网络号, 但是必须有不同的主机号
指IP协议中的路径规划功能,路由选择就是在a和b之间选择合适的路径(速度快设备开销小)这是一个探索的过程,相当于一步一步的问路。
路由表:描述了啥样子的ip从啥样的网络接口传输。(相当于给探索提供了一张地图)
数据链路层负责两个相邻节点之间的数据传输,比如从西安发往美国的快递,先发到北京(第一步从高速路到北京),再从北京空运到美国,这就相当于是考虑每一步具体怎么走。
关于不同的层,对于数据的命名也不同
传输层:数据段
网络层:数据报
数据链路层:数据帧
源地址和目的地址是指网卡的硬件地址(也叫MAC地址), 长度是48位,是在网卡出厂时固化的;
帧协议类型字段有三种值,分别对应IP、ARP、RARP;
帧末尾是CRC校验码
源地址和目的地址指的是mac地址,一个mac地址6个字节,ip地址是随机分配的,而mac地址是网卡出厂时候就被写死的,因此mac地址可以作为身份标识。
MTU相当于发快递时对包裹尺寸的限制. 这个限制是不同的数据链路对应的物理层, 产生的限制. 以太网帧中的数据长度规定最小46字节,最大1500字节,ARP数据包的长度不够46字节,要在后面补填充位; 最大值1500称为以太网的最大传输单元(MTU),不同的网络类型有不同的MTU; 如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的MTU了,则需要对数据包进行分片 , 不同的数据链路层标准的MTU是不同的 。
由于数据链路层MTU的限制, 对于较大的IP数据包要进行分包. 将较大的IP包分成多个小包, 并给每个小包打上标签; 每个小包IP协议头的 16位标识(id) 都是相同的;每个小包的IP协议头的3位标志字段中, 第2位置为0, 表示允许分片, 第3位来表示结束标记(当前是否是最后 一个小包, 是的话置为1, 否则置为0); 到达对端时再将这些小包, 会按顺序重组, 拼装到一起返回给传输层; 一旦这些小包中任意一个小包丢失, 接收端的重组就会失败. 但是IP层不会负责重新传输数据
一旦UDP携带的数据超过1472(1500 - 20(IP首部) - 8(UDP首部)), 那么就会在网络层分成多个IP数据报. 这多个IP数据报有任意一个丢失, 都会引起接收端网络层重组失败. 那么这就意味着, 如果UDP数据报在网 络层被分片, 整个数据被丢失的概率就大大增加了.
TCP的一个数据报也不能无限大, 还是受制于MTU. TCP的单个数据报的最大消息长度, 称为MSS(Max Segment Size); TCP在建立连接的过程中, 通信双方会进行MSS协商. 最理想的情况下, MSS的值正好是在IP不会被分片处理的最大长度(这个长度仍然是受制于数据链路层的 MTU). 双方在发送SYN的时候会在TCP头部写入自己能支持的MSS值. 然后双方得知对方的MSS值之后, 选择较小的作为最终MSS. MSS的值就是在TCP首部的40字节变长选项中(kind=2
在网络传输的具体过程中,a要把数据传给路由器1,就需要查a本身的路由表,查到路由器1的对应ip,arp协议便是设备在接入网络的时候广播一个ARP请求,收到请求设备返回ARP响应(就会包含mac和ip)
日常所见的网线(水晶头,灰色的线),网卡等。。。。
应用层的作用: 满足我们日常需求的网络程序, 都是在应用层 能够根据自己的需求, 设计应用层协议. 了解HTTP协议.
理解DNS的原理和工作流程.
传输层的作用: 负责数据能够从发送端传输接收端.
理解端口号的概念.
认识UDP协议, 了解UDP协议的特点.
认识TCP协议, 理解TCP协议的可靠性.
理解TCP协议的状态转化.连接管理, 确认应答, 超时重传, 滑动窗口, 流量控制, 拥塞控制, 延迟应答, 捎带应答特性.
理解TCP面向字节流,
理解粘包问题和解决方案.
能够基于UDP实现可靠传输.
理解MTU对UDP/TCP的影响.
网络层的作用: 在复杂的网络环境中确定一个合适的路径.
理解IP地址, 理解IP地址和MAC地址的区别.
理解IP协议格式.
了解网段划分方法 理解如何解决IP数目不足的问题.
理解私有IP和公网IP 理解网络层的IP地址路由过程.
理解一个数据包如何跨越网段到达最终目的地.
理解IP数据包分包的原因.
了解NAT设备的工作原理.
数据链路层的作用: 两个设备(同一种数据链路节点)之间进行传递数据
以太网是一种技术标准; 既包含了数据链路层的内容, 也包含了一些物理层的内容. 例如: 规定了网络拓扑结 构, 访问控制方式, 传输速率等;
以太网帧格式
理解mac地址
理解arp协议
理解MTU