分层结构
OSI七层模型
应用层
- 解决通过应用进程的交互来实现特定的网络应用问题
- 报文
常用协议
解决通信双方交换信息的表示问题
会话层的主要功能是在两个节点间建立、维护和释放面向用户的连接,并对会话进行管理和控制,保证会话数据可靠传送
传输层(运输层)
运行在不同主机上的应用进程提供直接的通信服务
- 包括
- 对数据分组组装
- 提供传输协议的选择
- 端口封装
- 差错校验
- tcp或udp报文
- 使用端口号区分不同的应用进程
- 用16bit表示取值为0~65535
- 熟知端口号:0~1023,IANA把这些端口号指派给了TCP/IP体系中的一些重要的协议,例如FTP:20/21
- 端口号只具有本地意义,即端口号只是为了表示本计算机中应用层中的各进程,在因特网中,不同计算机的相同端口号是没有联系的
常见协议
- TCP
-
网络层
作用
- IP地址编制(确定源IP和目的ip)
- 路由选择(选择路径)
- 静态路由(由管理员指定的,对路由器消耗小,配置复杂)
- 动态路由(根据路由协议判断,配置简单,路由器压力较大)
- 常见协议: IP、ARP(地址解析协议,通过解析IP地址得到Mac地址)
- ip数据包,分组
-
数据链路层
在单个链路上传输数据
- 如何标识网络中各主机的问题(主机编制问题,例如mac地址)
- 如何从信号表示的一连串比特流中区分出地址和数据(需要解决分组的封装格式问题)
- 如何协调各主机争用总线
- 分组 在一个网络(或一段链路上)传输的问题
- mac地址编制
- mac地址寻址
- 差错校验
- 网卡有独一无二的mac地址
- 帧
-
物理层
数据比特流的实际传输
- 需要解决的问题(传输比特流)
- 使用何种信号传输比特的问题
- 采用怎样的传输媒体(介质)比如双绞线网线
- 采用怎样的物理接口来连接传输媒体(例如RJ45以太网接口)
- 用怎样的信号来表示比特0和1(例如低电平0高电平1
- 电气特性定义(比如觉得网线中哪些线可以传输数据)
- 网线
-
tcp/ip四层模型
应用层
- 传输层
- 网络层(网际层)
-
五层模型
应用层
- 传输层(运输层)
- 网络层
- 数据链路层
-
TCP (传输控制协议Transmission Control Protocols)
特点
面向字节流, 这是tcp实现可靠传输、流量控制、拥塞控制的基础.在tcp发送数据时,从发送缓存取出一部分或全部字节并给其添加一个首部使之成为TCP报文段之后发送
- 发送方的tcp把应用层交付下来的数据块仅仅看作是一连串的、无结构的字节流,tcp并不知道这些待传送的字节流的含义仅将他们编号,并存储在自己的发送缓存中tcp根据发送策略,从缓存中提取一定数量的字节,构建tcp报文段并发送接收方的tcp,一方面从所接收到的tcp报文段中取出数据载荷部分,并存储在接收缓存中,一方面将接收缓存中的一些字节交付给应用进程,TCP不保证接收方应用进程所收到的数据块与发送方应用进程所发送的数据块具有对应大小关系
- 可靠传输、保证数据准确性、保证数据顺序
- 全双工通信. 在任何时候,单个TCP连接都允许同时双向传输数据,因此客户端和服务器端可以同时向对方发送数据
- 面向连接, TCP的连接是端到端的, 这意味着一个TCP连接只支持两方通信, 通常客户端为一方, 服务器端为一方
- 开销更大(相比udp)
- 必须先通过三次握手建立连接,然后数据传输,最后四次挥手释放连接
- 有拥塞控制机制
- 适用于要求可靠的传输应用
发送方维护一个拥塞窗口的cwnd的状态变量,其值取决于网络的拥塞程度,并且动态变化
拥塞窗口cwnd维护原则:
只要网络没有拥塞,拥塞窗口就再增大一些,但只要网络出现拥塞,拥塞窗口就减少一些
判断出现网络拥塞的依据: 没有按时收到应当到达的确认报文(即发生超时重传)
发送方将拥塞窗口作为发送窗口swnd,即swnd = cwnd
维护一个慢开始门限ssthresh状态变量:
当cwnd < ssthresh 使用慢开始算法
> 拥塞避免算法
= 既可以使用慢开始算法,也可以使用拥塞避免算法
重传计时器超时时,判断网络可能进入拥塞,进行以下操作
- 将ssthresh值更新为发生拥塞时,cwnd的一半
- 将cwnd减少为1并重新执行慢开始算法
慢开始 一般是指数增长 * 2到ssthresh, 慢开始是指一开始向网络注入的报文段少,而不是拥塞窗口cwnd增长慢
拥塞避免 一般是线性增长+1 ,并非指完全能够避免拥塞,而是指在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
有时候,个别报文段丢失,但实际上网络并没有拥塞,
这将导致发送方超时重传,并误以为网络发生了拥塞
发送方把拥塞窗口cwnd又设置为最小值1,并错误的启动慢开始算法,因而降低了传输效率
快重传算法,可以让发送方尽早知道发生了个别报文段的丢失
所谓快重传,就是使发送方尽快进行重传,而不是等超时计时器超时再重传
要求接收方不要等待自己发送数据时捎带确认,而是要立即发送确认
即使收到了失序的报文段,也要立即发出对已收到的报文段的全部确认
发送方一旦收到三个重复确认,就将相应的报文段立即重传,而不是等该报文段的超时计数器超时再重传
快恢复
三次握手
- 第一次握手:Client将SYN置1,随机产生一个初始序列号seq发送给Server,进入SYN_SENT状态,等待Ser确认
- 第二次握手:Server收到Client得SYN=1之后,知道客户端请求建立连接,将自己的SYN置1,ACK置1,产生一个acknowledge number = sequence number + 1,并随机产生一个自己的初始序列号,发送给客户端;进入SYN_RECV状态;
- 第三次握手 : 客户端检查acknowledge number 是否为序列号 + 1,ACK是否为1,检查正确后将自己的ACK置1,产生一个acknowledge number = 服务器发的序列号 + 1,发送给服务器;进入ESTABLISHED状态;服务器检查ACK为1和acknowledge number为序列号+1之后,也进入ESTABLISHED状态;完成三次握手,连接建立,Client、Server之间可以开始传输数据了

四次挥手
- 第一次挥手:Client 将FIN置1,发送一个序列号seq给Server;进入FIN_WAIT_1状态;
- 第二次挥手:Server收到FIN之后,发送一个ACK=1,acknowledge number = 收到的序列号+1;进入CLOSE_WAIT状态。此时客户端已经没有要发送的数据了,客户端变成FIN_WAIT_2状态,但仍可以接收服务器发来的数据
- 第三次挥手:Server将FIN置1,ACK置1,确认号为第一次seq+1,发送一个序列号给Client,用来关闭Server到Client的数据传输;进入LAST_ACK状态
- 第四次挥手:Client收到服务器的FIN后,进入TIME_WAIT状态;接着将ACK置1,发送一个acknowledge number = 序列号+1给服务器;服务器收到后,确认acknowledge number 后,变为CLOSED状态,不再向客户端发送数据。客户端等待2*MSL(报文段最长寿命)时间后,也进入CLOSED状态。完成四次挥手。
TCP报文段
- 序号:Seq序号,32位,用来标识从TCP源端口向目的端口发送的字节流,发送方发送数据时对此进行标记
- 确认号:Ack序号,32位,只有ACK标志位为1时,确认号字段才有效,Ack = Seq + 1
- 标志位:共六个,即URG、ACK、PSH、RST、SYN、FIN等
- URG:紧急指针(urgent pointer)有效
- ACK:确认序号有效
- PSH:接收方应该尽快将这个报文交给应用层
- RST:重置连接
- 在TCP协议中,rst段标识复位,用来异常的关闭连接。 在TCP的设计中它是不可或缺的,发送rst段关闭连接时,不必等缓冲区的数据都发送出去,直接丢弃缓冲区中的数据。 而接收端收到rst段后,也不必发送ack来确认
- SYN:发起一个新连接
- FIN:释放一个连接
流量控制
就是让发送方不要发送的快,要让接收方来得及接收
滑动窗口
tcp接收方利用自己接受端口的大小来限制发送方发送窗口的大小
tcp发送方收到接收方的零窗口通知时,应启动持续计时器,持续计时器超时后,向接收方发送零窗口探测报文
相关问题
为什么TCP是随机初始序列号
- 防止历史报文被下一个相同四元组的连接接收。
- 客户端和服务端建立一个 TCP 连接,在客户端发送数据包被网络阻塞了,而此时服务端的进程重启了,于是就会发送 RST 报文来断开连接。
- 紧接着,客户端又与服务端建立了与上一个连接相同四元组的连接;
- 在新连接建立完成后,上一个连接中被网络阻塞的数据包正好抵达了服务端,刚好该数据包的序列号正好是在服务端的接收窗口内,所以该数据包会被服务端正常接收,就会造成数据错乱。
防止伪造序列号进行攻击
防止服务器在收到失效的连接请求后,认为新的连接已经建立起来了,并一直等待客户端发来数据,这样的情况下,服务端的很多资源就没白白浪费掉了
为什么不能是三次挥手
如果没有第四次挥手,被动方不知道主动方有没有收到被动方发送的fin=1,也就一直不能断开
TCP粘包
指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方
发送端为了将多个发往接收端的包,更加高效的的发给接收端,于是采用了优化算法(Nagle算法),将多次间隔较小、数据量较小的数据,合并成一个数据量大的数据块,然后进行封包。那么这样一来,接收端就必须使用高效科学的拆包机制来分辨这些数据
粘包原因
TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:
- 只有上一个分组得到确认,才会发送下一个分组
- 收集多个小分组,在一个确认到来时一起发送
- Nagle算法造成了发送方可能会出现粘包问题
- TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。
什么时候需要处理粘包现象?
- 如果发送方发送的多组数据本来就是同一块数据的不同部分,比如说一个文件被分成多个部分发送,这时当然不需要处理粘包现象
如果多个分组毫不相干,甚至是并列关系,那么这个时候就一定要处理粘包现象了
如何处理粘包现象?
(1)发送方
对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。
(2)接收方
接收方没有办法来处理粘包现象,只能将问题交给应用层来处理
(2)应用层
应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。
解决办法:循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,但是如何判断每条数据的长度呢?格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符
- 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置
5.Q:UDP会不会产生粘包问题呢?
TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。
UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。
举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。四次挥手中客户端为什么等待2MSL再关闭
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包UDP(用户数据报协议User Datagram Protocols)
特点
- 无连接
- 不可靠
- 支持一对一、一对多、多对一、多对多通信
- 面向报文
- 无拥塞控制机制
首部:仅八个字节(2个源端口、2个目的端口、2个长度、2个校验和,其余是数据部分)
http/https
特点
区别
http信息明文传输,未加密不安全,80端口,速度更快,耗费资源更少
- https 更安全,是有ssl的加密传输协议,443端口
- HTTPS由于加密解密会带来更大的CPU和内存开销
-
长连接,短链接
http1.0默认短连接,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接
- HTTP/1.1起,默认使用长连接,用以保持连接特性
- 使用长连接的HTTP协议,会在响应头加入这行代码:
- Connection:keep-alive
- 在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接
HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接
版本
3.0
基于UDP,并引入QUIC协议使UDP实现了类似TCP的可靠传输
2.0
二进制格式:HTTP1的解析是基于文本格式的,HTTP2的解析是基于二进制格式的,效率更高
- 使用流和帧的概念实现了多路复用
- 头部压缩:HTTP1的头字段太长了,而且每次都重复发送,非常浪费带宽,HTTP2使用特别的算法在客户端和服务端建立字典记录之前发送的键值对,这样就能用索引号代替重复字符串,压缩效率极高。
服务端推送:HTTP1中服务端需要等待客户端请求才能被动响应,而HTTP2.0是允许服务端向客户端主动推送的
1.1
-
1.0
-
请求方法(PUT、POST、GET、DELETE)
get
幂等
- GET一般用于从服务器获取资源(查询)
缺点
不幂等
- 可以用来向HTTP服务器提交数据
- 提交请求时,参数则被封装在请求实体中传递,请求资源路径上不会出现参数信息
- 同样POST既可以创建对象,也可以修改对象。但用POST创建对象时,之前并不知道要操作的对象,由HTTP服务器为新创建的对象生成一个唯一的URI;使用POST修改已存在的对象时,一般只是修改目标对象的部分内容
- POST请求同PUT请求类似,都是向服务器端发送数据的,但是该请求会改变数据的种类等资源,就像数据库的insert操作一样,会创建新的内容。几乎目前所有的提交操作都是用POST请求的
- 一般用来创建资源
- 对数据类型没有要求,也允许二进制数据
-
put
使用PUT时,必须明确知道要操作的对象,如果对象不存在,创建对象;如果对象存在,则全部替换目标对象
- 幂等
- 可以用来向HTTP服务器提交数据
- 与GET不同的是,PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同
- 一般用来更新资源
POST主要作用在一个集合资源之上的(url),而PUT主要作用在一个具体资源之上的(url/xxx),通俗一下讲就是,如URL可以在客户端确定,那么可使用PUT,否则用POST
delete
DELETE请求顾名思义,就是用来删除某一个资源的,该请求就像数据库的delete操作
状态码
- 2开头表示成功, 200 请求被正常处理并返回
- 3开头表示需要进行附加操作以完成请求, 301 永久重定向 302暂时重定向
- 4客户端请求出错 400 Bad Request 401 Unauthorized 403Forbidden 404 Not Found
5服务器处理请求出错 500 服务器内部错误 501 服务不可用
其他
http怎么判断接收完整
客户端,一般用来保存用户信息的,比如之前登录了就不需要登录了,有大小限制(虽然HTTP本身对这个字段没有多少限制,但是Cookie最终要存储在浏览器上,所以不同的浏览器对Cookie的存储会有一些大小和个数上的限制)
- 客户端请求服务器时会将Cookie一起提交
- 服务器压力小
缺点:
服务器端,一般是通过服务端记录用户状态,比如购物车
- 优点
- 一般更安全
- 无大小限制
- 保存的值无类型限制
缺点
uri uniform resource identifier统一资源标识符
- url uniform resource locator 统一资源定位符
都可以唯一标识互联网上的资源,url是uri的一个子集,url以路径方式提供了定位资源的信息,
从输入网址到获得页面的过程 (越详细越好)?
浏览器查询 DNS,获取域名对应的IP地址
- 具体包括浏览器搜索自己的DNS缓存
- 搜索操作系统缓存
- 读取本地的Host文件
- 向本地DNS服务器进行查询等
- 浏览器获得域名对应的IP地址以后,向服务器请求建立链接,发起三次握手
- TCP/IP连接建立起来后,浏览器向服务器发送HTTP请求
- 服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器
- 浏览器解析并渲染视图,若遇到对js文件、css文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源
浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面
Socket通信
Socket套接字:对网络中不同主机上的应用进程直接进行双向通信的端点的抽象
- FD:file descriptor,文件描述符,非负整数,“一切皆文件”,Linux中一切资源都可以通过文件的方式进行管理和访问。而fd就类似于文件的索引(符号),指向某个资源,内核(kernel)利用FD来访问和管理资源
- 分为同步阻塞,异步方式等
select/poll:学生写完了作业会举手,但是你不知道是谁举手,需要一个个去询问
epoll:学生写完了作业会举手,你知道是谁举手,你直接去收作业
I/O多路复用
select
- 将socket是否就绪检查逻辑下沉到操作系统层次,避免大量的系统调用。告诉你事件就绪,但是没告诉你具体是哪个FD
- 优点:
- 不需要每个FD都进行一次系统调用,解决了频繁调用的用户态内核态切换的问题
缺点
和select基本类似,主要优化了监听1024的限制,入参的3个fd_set集合也不需要每次重置
epoll
高效处理并发下的大量连接,同时有非常优异的性能
- 维护了就绪列表,可以直接知道哪些文件描述符是就绪的
- 直接在内核态,维护了一个fd的红黑树
- 还维护了等待队列
- 缺点
- 跨平台性不够好,只支持linux,macos等操作系统不支持
- select相较于epoll更轻量,可移植性更强
- 在监听连接和事件比较少的情况下,select可能更优
- 水平(条件)触发LT(level-triggered):默认,epoll_wait检测到事件后,如果该事件没被处理完毕,后续每次epoll_wait调用都会返回该事件
- 边缘触发(ET:Edge-triggered):epoll_wait检测到事件后,只会在当次返回该事件,不管该事件是否处理完毕
