OSI七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层;
TCP/IP四层模型:数据链路层、网络层、传输层、应用层。
链路层:负责封装和解封装IP报文,发送和接受ARP/RARP报文等。
网络层:负责路由以及把分组报文发送给目标网络或主机。
传输层:负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文。
应用层:负责向用户提供应用程序,比如HTTP、FTP、Telnet、DNS、SMTP等。
TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议。
这个协议的重点是面向连接。他其中包含的内容都是和连接相关,首先介绍就是建立连接和断开连接的过程
各部分的含义:
源端口号/目的端口号: 表示数据从哪个进程来, 到哪个进程去。
32位序号: 这些字段被 TCP 发送方和接收方用来实现可靠的数据传输。
4位首部长度: 表示该tcp报头有多少个4字节(32个bit)。
6位保留: 顾名思义, 先保留着, 以防万一。
URG: 标识紧急指针是否有效。
ACK: 标识确认序号是否有效。
PSH: 用来提示接收端应用程序立刻将数据从tcp缓冲区读走。
RST: 要求重新建立连接. 我们把含有RST标识的报文称为复位报文段。
SYN: 请求建立连接. 我们把含有SYN标识的报文称为同步报文段。
FIN: 通知对端, 本端即将关闭. 我们把含有FIN标识的报文称为结束报文段。
16位窗口大小: 这个字段用于流量控制。它用于指示接收方能够/愿意接受的字节数量。
16位检验和: 由发送端填充, 检验形式有CRC校验等. 如果接收端校验不通过, 则认为数据有问题. 此处的校验和不光包含TCP首部, 也包含TCP数据部分。
16位紧急指针: 用来标识哪部分数据是紧急数据。
端口号
数据链路和 IP 中的地址,分别指的是 MAC 地址和 IP 地址。前者用来识别同一链路中不同的计算机,后者用来识别 TCP/IP 网络中互连的主机和路由器。在传输层也有这种类似于地址的概念,这就是端口号。端口号用来识别同一台计算机中进行通信的不同应用程序。因此,它也被称为程序地址。
0-1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议。
1024-65535:操作系统动态分配的端口号,客户端程序的端口号,由操作系统从这个范围分配
常见端口号:
SSH服务器:22端口
FTP服务器:21端口
Telnet服务器:23端口
HTTP服务器:80端口
HTTPS服务器:443端口
三次握手的作用是:在进行通信前,确定客户端和服务端的收、发能力正常。
第一次握手:客户端发送包含SYN和seq的网络包,然后服务端收到了。客户端进入SYN_SEND状态;
第二次握手:服务端发送包含SYN、ACK和seq的网络包,然后客户端收到了。服务器端进入SYN_RECEIVED状态
第三次握手:客户端发送包含ACK和seq的网络包,然后服务端收到了。客户端和服务器端都进入ESTABLISHED状态
面试题:两次握手可以吗?
客户端
发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达服务器端
。本来这是一个早已失效的报文段。但服务器端
收到此失效的连接请求报文段后,就误认为是 客户端
再次发出的一个新的连接请求。于是就向客户端
发出确认报文段,同意建立连接。服务器端
发出确认,新的连接就建立了。由于现在客户端
并没有发出建立连接的请求,因此不会理睬 服务器端
的确认,也不会向服务器端
发送数据。但服务器端
却以为新的运输连接已经建立,并一直等待客户端
发来数据。这样,服务器端
的很多资源就白白浪费掉了。采用 “三次握手” 的办法可以防止上述现象发生。例如刚才那种情况,客户端
不会向服务器端
的确认发出确认。服务器端
由于收不到确认,就知道客户端
并没有要求建立连接。客户端
正确接收第二次握手的报文(服务器端
无法确认客户端
是否收到),也无法保证客户端
和服务器端
之间成功互换初始序列号。面试题: 四次握手可以吗?
面试题: 第三次握手中,如果客户端的ACK未送达服务器,会怎样?
服务器端:由于服务器端没有收到ACK确认,因此会每隔 3秒 重发之前的SYN+ACK(默认重发五次,之后自动关闭连接进入CLOSED状态),客户端收到后会重新传ACK给服务器端。
客户端,会出现两种情况:
面试题: 如果已经建立了连接,但客户端出现了故障怎么办?
面试题: 初始序列号是什么?
数据传输结束后,通信的双方可以释放连接。数据传输结束后的客户端主机和服务端主机都处于 ESTABLISHED 状态,然后进入释放连接的过程。
第一次挥手: 客户端将FIN置为1,发送一个序列号seq给服务器端;进入FIN_WAIT_1状态;
第二次挥手: 服务器端收到FIN之后,发送一个ACK=1,acknowledge number=收到的序列号+1;进入CLOSE_WAIT状态。此时客户端已经没有要发送的数据了,但仍可以接受服务器发来的数据。
第三次挥手: 服务器端将FIN置1,发送一个序列号给客户端;进入LAST_ACK状态;
第四次挥手: 客户端收到服务器的FIN后,进入TIME_WAIT状态;接着将ACK置1,发送一个acknowledge number=序列号+1给服务器;服务器收到后,确认acknowledge number后,变为CLOSED状态,不再向客户端发送数据。客户端等待2*MSL(报文段最长寿命)时间后,也进入CLOSED状态。完成四次挥手。
用现实理解四次挥手流程:
面试题: 为什么不能把服务器发送的ACK和FIN合并起来,变成三次挥手(CLOSE_WAIT状态意义是什么)?
面试题: 如果第二次挥手时服务器的ACK没有送达客户端,会怎样?
面试题 :客户端TIME_WAIT状态的作用是什么?
服务器端
没有收到ACK,就会重发FIN,如果客户端
在2*MSL的时间内收到了FIN,就会重新发送ACK并再次等待2MSL,防止服务器端
没有收到ACK而不断重发FIN。 MSL(Maximum Segment Lifetime),指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,客户端
都没有再次收到FIN,那么客户端
推断ACK已经被成功接收,则结束TCP连接。TCP将每个字节的数据都进行了编号, 即为序列号:
每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你要从哪里开始发。
主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B,如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发,但是主机A没收到确认应答也可能是ACK丢失了。
因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉. 这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果。
超时时间如何确定?
这样一发一收的方式性能较低, 那么我们一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。
窗口
窗口大小指的是无需等待确认应答就可以继续发送数据的最大值,上图的窗口大小就是4000个字节 (四个段).
发送前四个段的时候, 不需要等待任何ACK, 直接发送,收到第一个ACK确认应答后, 窗口向后移动, 继续发送第五六七八段的数据…
因为这个窗口不断向后滑动, 所以叫做滑动窗口,操作系统内核为了维护这个滑动窗口, 需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只有ACK确认应答过的数据, 才能从缓冲区删掉。
接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被填满, 这个时候如果发送端继续发送, 就会造成丢包, 进而引起丢包重传等一系列连锁反应.
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度.这个机制就叫做 流量控制(Flow Control)
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段,通过ACK通知发送端;
窗口大小越大, 说明网络的吞吐量越高;
接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
发送端接受到这个窗口大小的通知之后, 就会减慢自己的发送速度;如果接收端缓冲区满了, 就会将窗口置为0;这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 让接收端把窗口大小再告诉发送端.
接收端如何把窗口大小告诉发送端呢? 在我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息;
那么问题来了, 16位数字最大表示65535, 那么TCP窗口最大就是65535字节么?
实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位(左移一位相当于乘以2).
虽然TCP有了滑动窗口, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题.
因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据, 是很有可能引起雪上加霜的.
TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;
此处引入一个概念程为拥塞窗口
慢启动
的阈值
当TCP开始启动的时候, 慢启动阈值等于窗口最大值;
在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;
少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案.
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小.
假设接收端缓冲区为1M. 一次收到了500K的数据;
如果立刻应答, 返回的窗口大小就是500K;
但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了; 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;
如果接收端稍微等一会儿再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M
窗口越大, 网络吞吐量就越大, 传输效率就越高.
TCP的目标是在保证网络不拥堵的情况下尽量提高传输效率;
那么所有的数据包都可以延迟应答么?
数量限制: 每隔N个包就应答一次
时间限制: 超过最大延迟时间就应答一次
具体的数量N和最大延迟时间, 依操作系统不同也有差异,一般 N 取2, 最大延迟时间取200ms.
在延迟应答的基础上, 我们发现, 很多情况下客户端和服务器在应用层也是 “一发一收” 的:
意味着客户端给服务器说了 “How are you”
服务器也会给客户端回一个 “Fine, thank you”
那么这个时候ACK就可以搭顺风车, 和服务器回应的 “Fine, thank you” 一起发送给客户端。
创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;
调用write时, 数据会先写入发送缓冲区中;
如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区; 然后应用程序可以调用read从接收缓冲区拿数据;
另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工
由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:
写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;
首先要明确, 粘包问题中的 “包”, 是指应用层的数据包.
在TCP的协议头中, 没有如同UDP一样的 “报文长度” 字段
但是有一个序号字段.
站在传输层的角度, TCP是一个一个报文传过来的. 按照序号排好序放在缓冲区中.
站在应用层的角度, 看到的只是一串连续的字节数据.
那么应用程序看到了这一连串的字节数据, 就不知道从哪个部分开始到哪个部分是一个完整的应用层数据包.
此时数据之间就没有了边界, 就产生了粘包问题
那么如何避免粘包问题呢?
归根结底就是一句话, 明确两个包之间的边界
对于定长的包
对于变长的包
进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同.
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行 reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.
另外, 应用层的某些协议, 也有一些这样的检测机制.
例如HTTP长连接中, 也会定期检测对方的状态.
例如QQ, 在QQ断线之后, 也会定期尝试重新连接.
1.保证可靠性的机制
校验和
序列号(按序到达)
确认应答
超时重传
连接管理
流量控制
拥塞控制
2.提高性能的机制
滑动窗口
快速重传
延迟应答
捎带应答
3.基于TCP应用层协议
HTTP
HTTPS
SSH
Telnet
FTP
SMTP
源端口(可选字段):收端的应用程序利用这个字段的值作为发送响应的目的地址;
目标端口号: 表示接收端端口;
UDP长度: 表示整个数据报(UDP首部+UDP数据)的最大长度;
整个数据报文的检验和(IPv4 可选 字段),该字段用于发现头部信息和数据中的错误。如果校验和出错, 就会直接丢弃。
因此 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。
知道对应端的IP和端口号就直接进行传输, 不需要建立连接。
具体来说就是:
没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息;
不能够灵活的控制读写数据的次数和数量
面试题:对于UDP协议来说, 是否也存在 “粘包问题” 呢?
对于UDP, 如果还没有向上层交付数据, UDP的报文长度仍然存在.
同时, UDP是一个一个把数据交付给应用层的, 就有很明确的数据边界.
站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收.
不会出现收到 “半个” 的情况.
UDP能够传输的数据最大长度不能超过64K,由16位UDP长度所限制。
属于网络层的协议
主机: 配有IP地址, 但是不进行路由控制的设备; 路由器: 即配有IP地址, 又能进行路由控制;
节点: 主机和路由器的统称;
4位版本号:指定IP协议版本,如IPV4,IPV6
4位首部长度:IP头部长度有多少个32bit,即length4字节,4bit(1111)最大为15,所以IP头部最大长度为60字节
8位服务类型:3位优先权字段(弃用),4位TOS字段和1位保留字(必须置为0),4位TOS表示最小时延,最大吞吐量,最高可靠性,最小成本,四者相互冲突只能选一个。
16位总长度:IP数据报整体占多少字节
8位生存时间(Time To Live):数据报到达目的地最大报文跳数,每经过一个路由,TTL-1,一直减到0还没到就弃用了,主要为了防止路由循环。
8位协议,主要表示上层协议类型
16位头部校验和:使用CRC进行校验,鉴别头部是否损坏
32位源地址和32位目标地址:表示发送端和接收端
1.IP地址分为两部分,网络号和主机号,网络号保证互连的两个网段具有不同的标识。主机号:同一网段内,主机之间具有相同的网络号,但是必须有不同的主机号.
2.五类IP地址
A类0.0.0.0到127.255.255.255
B类128.0.0.0到191.255.255.255
C类192.0.0.0到223.255.255.255
D类224.0.0.0到239.255.255.255
E类240.0.0.0到247.255.255.255
避免大量的IP地址被浪费,引入额外的子网掩码来区分网络号和主机号。
1.子网掩码也是一个32位的正整数. 通常用一串 “0” 来结尾;
2.将IP地址和子网掩码进行 “按位与” 操作, 得到的结果就是网络号;
3.网络号和主机号的划分与这个IP地址是A类、B类还是C类无关;
将IP地址中的主机地址全部设为0, 就成为了网络号, 代表这个局域网;
将IP地址中的主机地址全部设为1, 就成为了广播地址, 用于给同一个链路中相互连接的所有主机发送数据包;
127.*的IP地址用于本机环回(loop back)测试,通常是127.0.0.1
完!