1.1 socket运行流程
- 客户端和服务器分别使用
socket()
函数创建socketfd
,需要指定地址族(IPv4或IPv6)、套接字类型(TCP或UDP)和协议(通常为0,表示使用默认协议)等 - 服务器使用
bind()
函数绑定字节的IP地址和端口号,并使用listen()
函数监听连接请求 - 客户端使用
connect()
函数连接服务器,需要指定服务器的IP和端口 - 连接建立后,服务器在有请求发生时使用
accept()
函数进行接受 - 客户端使用
send()
函数发送数据,服务器端使用recv()
函数接收数据 - 数据传输完成后,客户端和服务器端可以调用
close()
函数关闭Socket连接,释放相关资源
1.2 Linux接收、发布网络包的流程
- 接收网络包流程:
- 网卡接收网络包,通过DMA技术写入到ring buffer,通过触发中断告知OS已到达,进入网络接口层向上进入网络层、传输层,根据四元组找到对应的socket,把数据放到socket接收缓冲区,应用程序调用socket接口将数据拷贝到应用层缓冲区
- 发送网络包流程:
- 应用程序调用socket从用户态到内核态把数据拷贝到sk_buff中并放到socket的发送缓冲区,到达传输层若使用TCP传输会先拷贝一个新的sk_buff副本用来保障可靠性,然后传入网络层、网络接口层,通过ARP获得下一跳的MAC地址然后放到网卡发送队列,触发软中断读取sk_buff到ring buffer,然后映射到网卡可访问的DMA区域,触发真实发送,发送完成后网卡硬中断释放sk_buff和ring buffer,受到TCP的ACK报文后传输层释放sk_buff副本
1.3 发送网络数据的时候,涉及几次内存拷贝操作
- 三次
- socket系统调用时将数据拷贝到socket发送缓冲区
- TCP为保障可靠性在传输层到网络层每层sk_buff都拷贝一个副本
- 网络层需要分包时会将原来的sk_buff拷贝为多个小的sk_buff
1.4 介绍select poll epoll的区别
- 本质上都是同步IO,因为他们都需要在读写事件就绪后自己负责进行读写,即读写过程是阻塞的
- select使用线性表,监听端口的大小有限,32位机默认1024个,每次调用都是将整个数据结构从用户态拷贝到内核,而且需要遍历整个fd集合来判断是否有fd就绪,时间复杂度O(n)
- poll使用链表,没有了最大连接数的限制,每次调用和select一样,而且只支持水平触发,即如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd
- epoll可以理解为event poll,是事件驱动的,时间复杂度O(1),没有最大并发连接的限制,不采用轮询的方式查询活跃的fd,而是活跃的fd调用callback,并放入就绪列表中等待处理,消息传递通过mmap()让内核和用户空间共享一块内存,支持边缘触发,即调用epoll_wait()只会通知一次,直到该fd第二次有可读写事件才会通知你
1.5 在浏览器中输⼊URL并按下回⻋之后会发⽣什么
- 输入URL,DNS域名解析将域名解析成对应的IP地址,建立TCP连接,浏览器发送http请求给web服务器,服务器处理请求并返回http响应报文,浏览器渲染页面,断开TCP连接
1.6 DNS解析过程
- 先查询浏览器缓存有没有改域名对应的IP地址
- 在计算机本地的host文件查找
- 向本地DNS服务器发送一个DNS查询请求,没有则向根DNS服务器发送
- 根DNS服务器负责告诉向哪个顶级DNS服务器查询
- 本地DNS服务器向顶级DNS服务器发送请求,顶级DNS负责告诉向哪个权威DNS服务器发送请求
- 本地DNS服务器向权威DNS服务器发送请求,它会查找IP并返回给本地DNS服务器
- 本地DNS服务器将IP返回给浏览器,并缓存在本地
1.7 DNS采用集中式会有什么问题
- 单点故障、对于远距离用户会造成高延迟、维护成本高需要频繁更新
1.8 迭代查询和递归查询的区别
- 迭代查询:DNS客户端向上层服务器发送查询请求但不要求直接提供完整的解析结果而是询问更高级域名服务器的地址
- 递归查询:DNS客户端向上层DNS服务器发送查询请求并要求服务器提供完整的解析结果,上层服务器自行向上查询
1.9 HTTP协议版本演进介绍
- http 0.9:
- 只支持get方法,没有请求头,服务器只能返回html格式的内容
- http 1.0:
- 引入了请求头和响应头,支持多种请求方法和状态码
- 不支持持久连接
- http 1.1:
- 支持长连接
- 管道网络输送:一个TCP连接里可以发送多个请求,第一个请求发送出去了不需要等待响应就可以发送第二个请求
- 队头阻塞:一个请求被阻塞,后面的请求全被阻塞
- 服务器只能被动响应
- http 2:
- 头部压缩:使用HOACK压缩算法对请求头和响应头进行压缩
- 二进制帧
- 并发传输,引入stream概念,不同的http请求用stream id来区分,不同的stream帧可以乱序发送在接收端组转
- 服务器推送
- 设置stream优先级,提高用户体验
- 传输层队头阻塞:TCP要保证可靠性就要等受到的数据完整且连续才会返回给http应用
- 网络迁移需要重新连接
- http 3:
- 基于UDP协议在应⽤层实现了 QUIC 协议来保障可靠性
- 0-RTT连接:在第二次连接时进⾏零往返时间连接建⽴
- 无队头阻塞
- 连接迁移:QUIC通过ID来标记通信的两个端点,网络变化了,IP变化了,但仍存在上下文,就可以无缝复原连接
- 向前纠错机制:每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装⽽⽆需重传
1.10 HTTP与HTTPS的区别
- HTTP使用明文传输,HTTPS在应用层和传输层之间加了SSL/TLS安全协议,能够报文加密
- HTTPS在TCP三次握手之后还需进行SSL/TLS握手过程
- HTTP端口号80,HTTPS端口号443
- HTTPS需要向CA申请数字证书来保证服务器身份的可信
1.11 HTTPS建立过程
- 客户端发送https请求向443端口,请求与服务器建立连接
- 服务器产生一对公私钥,然后将公钥发给CA机构,CA也有一对公私钥,用自己的私钥将服务器的公钥进行加密产生一个CA数字证书
- 服务器将CA证书发送给客户端
- 客户端解析CA证书(浏览器已经保存了大部分CA机构的密钥),验证合法性,如果合法从中取出服务端生成的公钥,并生成一个随机码,即为对称加密用的密钥
- 客户端将随机码发给服务器,服务器收到后用自己的私钥解密得到随机码
- 双方使用随机码进行加密传输数据
1.12 对称加密和非对称加密
1.13 HTTP缓存
- 强制缓存:判断是否使用缓存取决于浏览去,如果命中缓存直接从内存中读取,无需与服务器通信
- expires强缓存:通过设置一个强缓存时间实现,使用的是本地时间戳,不准就会有漏洞
- cache-control强缓存:在资源的响应头上写上需要缓存多久,单位是秒
- 协商缓存:通过服务器告知浏览器是否使用缓存
- last-modified 和 if-modified-since:根据文件修改时间判断是否缓存有效
- etag:根据文件内容计算出一个哈希值来判断,有强验证和弱验证两种
- 协商缓存这两个字段都需要配合强制缓存中 Cache-Control 来使⽤,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求,设置
Cache-control:no-cache
1.14 post和get的区别
- get请求参数在url地址上,直接暴露,有长度限制(浏览器的限制),post请求的参数放在body部分
- get产生一个TCP数据包,会把http头和数据一起发出;post产生两个TCP数据包,先发送头再发送数据
- get会被浏览器主动缓存,post不会除非手动设置
- get的效率比post高
1.15 TCP与UDP的区别
- 可靠性:
- TCP通过序列号、重传机制、滑动窗口、流量控制、拥塞控制等机制确保数据的正确性和完整性
- UDP是不提供可靠性保证,数据报文发送后不关系是否被接收或顺序是否正确
- 连接性:
- TCP是面向连接的协议,需要建立TCP三次握手建立连接
- UDP是无连接的协议,每个数据包都是独立的
- 效率:
- TCP开销较大,适用于对数据完整性高的应用场景,比如发送邮件,浏览器访问等
- UDP传输效率高额外开销小,适用于实时性较高的应用场景,比如音视频等
1.16 TCP三次握手和四次挥手
- 三次握手:
- 第一次:(SYN)
- 客户端随机初始化序列号client_isn放进TCP首部,SYN置1,然后把SYN报文发给服务器,之后客户端处于SUN-SENT状态
- 第二次:(SYN+ACK)
- 服务器收到SYN报文后,把自己的随机初始化序列号server_isn放进TCP首段,确认应答号client_isn+1,SYN和ACK置1,然后把SYN+ACK报文发给客户端,之后服务器处于SYN-RECV状态
- 第三次:(ACK)
- 客户端收到报文后向服务器回应一个应答报文,ACK置1,确认应答号server_isn+1,发送给服务器,这次报文可以携带数据,之后客户端处于ESTABLISHED状态;服务器收到应答后,也进入ESTABLISHED状态
- 第一次:(SYN)
- 四次挥手:
- 在挥手之前,客户端和服务器都处于ESTABLISHED状态
- 第一次:(FIN)
- 客户端发送一个FIN置1的报文给服务端,此时客户端处于FIN_WAIT1状态
- 第二次:(ACK)
- 服务器收到后向客户端发送ACK报文,把客户端序列号+1,此时服务器处于CLOSE_WAIT状态
- 第三次:(FIN)
- 等待服务器处理完数据后,向客户端发送FIN报文,此时服务器处于LAST_ACK状态
- 第四次:(ACK)
- 客户端收到FIN报文后回一个ACK报文,之后客户端处于TIME_WAIT状态;服务器收到ACK报文后进入CLOSE状态
- 客户端经过2MSL后自动进入CLOSE状态
1.17 TCP连接如何确保可靠性(重传、滑动窗口、流量控制、拥塞控制)
- 重传机制:在TCP中,发送端数据到达接收端时会返回一个确认应答消息,如果传输过程中数据包丢失就会使用重传机制
- 超时重传:设定一个计时器,超过这个时间没有收到对方的ACK报文就会重发该消息。如果重发的数据再次超时会将超时间隔加倍
- 快速重传:以数据驱动,当收到三个相同的ACK报文时,会在定时器过期之前重传丢失的报文段
- 滑动窗口:窗口就是无需等待确认应答可以继续发送数据的最大值
- 通常窗口的大小由接收方决定,发送方发送数据大小不能超过接收方大小,否则接收方就无法正常接收到数据
- 流量控制:通过滑动窗口机制,接收方可以通过调整窗口大小告诉发送方当前处理数据的能力
- 拥塞控制:网络拥堵会导致数据包延时或丢失,这时TCP会进行重传,就会出现恶性循环,拥塞控制目的就是避免发送方的数据填满整个网络
- 慢启动:TCP刚建立连接后先进行慢启动过程,发送方每收到一个ACK拥塞窗口的大小+1,发包的个数指数性增长,当发包数量大于等于门限时就会使用拥塞避免算法
- 拥塞避免:每当收到一个ACK时,拥塞窗口增加其大小分之1,这样将指数增长变成了线性增长,增长速度放缓。随之就会出现拥塞的状况导致丢包,就出发了重传机制
- 拥塞发生:
- 如果发生的是超时重传,门限设置为拥塞窗口大小的一般,拥塞窗口大小重置为1,接着就会重新开始慢启动过程。由于是突然减少数据流,会造成网络卡顿
- 如果发生的是快速重传,TCP认为只丢失了一部分,拥塞窗口大小变为原来的一半,门限等于拥塞窗口的大小,进入快速恢复算法
- 快速恢复:
- 还能收到3个重复的ACK说明网络不是很拥堵,拥塞窗口设置为门限大小+3(3是因为确认有3个数据包收到了),然后重传丢失的数据包。
- 如果再收到重复的ACK则拥塞窗口+1;
- 如果收到新数据的ACK则把窗口设置为之前的门限值,恢复过程已经结束再次进入拥塞避免状态,后续继续线性增长
- 还能收到3个重复的ACK说明网络不是很拥堵,拥塞窗口设置为门限大小+3(3是因为确认有3个数据包收到了),然后重传丢失的数据包。
1.18 怎么⽤UDP实现可靠传输
- 在应用层实现数据的可靠性传输,模拟TCP可靠性传输方式,如确认机制、重传机制、校验机制等方式来保证数据可靠性传输。
- 例如,KCP协议,它能以比TCP浪费10%-20%的带宽的代价,换取平均延迟降低30%-40%,且最大延迟降低三倍的传输效果
1.19 TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是⼀个东⻄吗?
- 不是,HTTP 的 Keep-Alive,是由应用层(用户态)实现的,称为 HTTP 长连接;TCP 的 Keepalive,是由 TCP 层(传输层、内核态)实现的,称为 TCP 保活机制
- HTTP的Keep-Alive可以使得用同一个 TCP 连接来发送和接收多个 HTTP 请求
- TCP的Keep-Alive是当客户端和服务端长达一定时间没有进行数据交互时,内核为了确保该连接是否还有效,就会发送探测报文,来检测对方是否还在线,然后来决定是否要关闭该连接。
1.20 Cookie、Session、Token区别
- cookie:
- 在客户端存储数据的机制,由服务器通过set-cookie响应头发送给客户端浏览器,用于在客户端保持用户状态和存储少量数据,可能会存在安全风险和存储容量受限
- seesion:
- 在服务器存储用户状态和数据的机制,通过ID来管理每个客户端,用于在服务器上跟踪用户、存储数据和身份认证,安全性较高,但会增加服务器的负担
- token:
- 在客户端和服务器之间传递信息的机制,作为身份验证和授权的凭证
- 如何使用token保证用户不能同时登录:key-用户名,value-token,客户端每次发送请求都把token放在请求头,服务器校验token如果不符合就覆盖原来的token,先登录的就无法继续操作了
1.21 半连接队列、SYN泛洪和避免方案
- 半连接队列:也叫SYN队列,服务器第一次收到客户端的SYN后会处于SYN_RCVD状态,此时双方还没完全建立连接,服务器会把这些请求放在一个队列里
- 全连接队列:也叫ACCEPT队列,用于存放已经完成三次握手建立好连接状态的连接
- SYN泛洪流:攻击者发送大量伪造的SYN请求到服务器但不完成后续握手过程,导致半连接队列已满,后续再收到SYN报文就会丢弃,无法与客户端正确进行连接
- 如何避免:
- 使用SYN-cookie:当服务器收到一个SYN报文后不立即分配缓冲区,而是利用连接信息生成一个cookie并作为将要返回的SYN+ACK报文的初始序列号。当客户端返回一个ACK报文后根据头部信息计算cookie,看是否是初始序列号+1
1.22 TCP粘包拆包问题,UDP会粘包吗
- TCP有粘包问题,UDP没有粘包问题
- 问题:
- TCP是面向流的协议,发送端可以一次发送2KB的数据,接收端可以一次接收3KB或更多的数据,这就会让发送端使用Nagle优化算法将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包,接收端进行拆包,面向流的通信是无消息保护边界的
- UDP是面向消息的协议,每个UDP段都是一条消息,应用程序都以消息为单位提取数据,面向消息的通信是有消息保护边界的
- 解决:
- 发送端发数据前,先将待发的数据长度告知接收端。将数据长度放在一个固定长度的字节中发给接收端;接收端先接收这个固定长度的数据头,从这个数据头中获悉待接收数据的长度,做好循环接收的准备
1.23 HTTP状态码
- 1xx,协议处理的中间状态
- 2xx,服务器成功处理了客户端的请求
- 3xx,客户端请求的资源发生了变动,需要重定向
- 4xx,客户端发送的报文有误,服务器无法处理(错误码)
- 5xx,客户端请求报文正确,但是服务器处理时内部发生了错误
1.24 为什么需要三次握手,两次或四次不行吗?
- 主因:可以防止历史连接
- 网络出现阻塞导致客户端向服务器发送了两次SYN报文,旧的SYN到达服务器后会回复一个ACK+SYN给客户端,客户端再收到后知道这时旧的会发送RST报文给服务端,服务端会中止这次连接等待新的SYN报文
- 如果只是两次握手,服务端在收到SYN后会进入到ESTABLISHED状态直接建立连接并发送数据,但客户端知道这是历史连接会发送RST报文来断开连接
- 次因:三次握手可以同步双方的初始序列号,两次握手只能保证一方的初始序列号能被对方接收,四次握手也能保证双方的初始化序列号同步但可以省略成三次
1.25 为什么需要TIME_WAIT状态?为什么TIME_WAIT等待时间是2MSL?
1.26 OSI七层模型、TCP/IP四层模型、五层网络体系结构对比
- 物理层:负责物理媒介的传输,例如电缆、光纤或无线信号,传输的数据是比特流
- 数据链路层:逻辑链接、硬件地址寻址,用MAC地址访问介质,传输单位是帧
- 网络层:数据的路由和转发,传输单位是数据报,常见协议有IP、ICMP、ARP
- 传输层:端到端的数据传输服务,常见协议是TCP和UDP
- 会话层:会话连接、维护和终止
- 表示层:将计算机能识别的东西转换成人能识别的东西,比如图片、声音
- 应用层:为用户程序提供网络服务,比如HTTP、DNS