第二部分:应用层

  • Q:GET 和 POST 的区别
    • get提交的数据会放在URL后,并且请求参数会被完整的保留在浏览器的记录里,由于参数直接暴露在URL中,可能会存在安全问题,因此往往用于获取资源信息。而post参数放在请求主体中,并且参数不会被保留,相比get方法,post方法更安全,主要用于修改服务器上的资源
    • get请求只支持URL编码,post请求支持多种编码格式
    • get只支持ASCII字符格式的参数,而post方法没有限制
    • get提交的数据大小有限制(主要针对浏览器而言),而post方法提交的数据没有限制
  • Q:HTTPS建立连接的过程
    • 1.客户端发起一个HTTPS请求,并连接到服务器的443端口,发送的信息主要包括自身所支持的算法列表和密钥长度等
    • 2.服务端将自身所支持的所有加密算法和客户端的算法列表进行对比并选择一种支持的加密算法,然后将它和其它密钥组件一同发送给客户端
    • 3.服务器向客户端发送一个包含数字证书的报文,该数字证书中包含证书的颁发机构、过期时间、服务端的公钥等信息
    • 4.最后服务端发送一个报文通知客户端SSL的第一阶段已经协商完成
    • 5.SSL第一次协商完成后,客户端发送一个回应报文,报文中包含一个客户端生成的随机密码串,称为pre_master_secre,并且该报文是经过证书中的公钥加密过的
    • 6.紧接着客户端会发送一个报文提示服务端在此之后的报文是采用pre_master_secre加密的
    • 7.客户端向服务端发送一个finish报文,这次握手中包含第一次握手至今所有报文的整体校验值,最终协商是否完成取决于服务端能否成功解密
    • 8.服务端同样发送与第6步中相同作用的报文,已让客户端进行确认,最后发送finish报文告诉客户端自己能够正确解密报文
    • 当服务端和客户端的finish报文交换完成后,SSL连接就算建立完成了,之后就进行和HTTP相同的通信过程,唯一不同的是在HTTP通信过程中并不是采用明文传输,而是采用对称加密的方式,其中对称密钥已经在 SSL 的建立过程中协商好了
  • Q:HTTPS和HTTP的区别
    • HTTP 协议以明文方式发送内容,数据都是未加密的,安全性较差。HTTPS 数据传输过程是加密的,安全性较好
    • HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80 端口,后者是 443 端口
    • HTTPS 协议需要到数字认证机构(Certificate Authority, CA)申请证书,一般需要一定的费用
    • HTTP 页面响应比 HTTPS 快,主要因为 HTTP 使用 3 次握手建立连接,客户端和服务器需要握手 3 次,而 HTTPS 除了 TCP 的 3 次握手,还需要经历一个 SSL 协商过程
  • Q:HTTPS的加密方式
    • HTTPS采用对称加密和非对称加密相结合的方式,首先使用SSL/TLS协议进行加密传输,为了弥补非对称加密的缺点,HTTPS采用证书来进一步加强非对称加密的安全性,通过非对称加密,客户端和服务端协商好之后进行通信传输的对称密钥,后续的所有信息都通过该对称密钥进行加密解密,完成整个HTTPS的流程
  • Q:客户端为什么信任第三方证书
    • 假设中间人篡改了整数原文,由于他没有CA机构的私钥,所以无法得到此时加密后的签名,因此无法篡改签名。客户端浏览器收到该证书后会发现原文和签名解密后的值不一致,则说明证书被中间人篡改,证书不可信,从而终止向服务器传输信息
    • 上述过程说明证书无法被篡改,我们考虑更严重的情况,例如中间人拿到了 CA 机构认证的证书,它想窃取网站 A 发送给客户端的信息,于是它成为中间人拦截到了 A 传给客户端的证书,然后将其替换为自己的证书。此时客户端浏览器收到的是被中间人掉包后的证书,但由于证书里包含了客户端请求的网站信息,因此客户端浏览器只需要把证书里的域名与自己请求的域名比对一下就知道证书有没有被掉包了
  • Q:HTTP 是不保存状态的协议,如何保存用户状态
    • 基于 Session 实现的会话保持
      • 在客户端第一次向服务器发送HTTP请求后,服务器会创建一个session对象并将客户端的身份信息以键值对的形式存储下来,然后分配一个会话标识(SessionID)给客户端,这个会话标识一般保存在客户端Cookie中,之后每次该浏览器发送HTTP请求都会带上Cookie中的SessionId到服务器,服务器根据会话标识就可将之前的状态信息与会话联系起来,从而实现会话保持
      • 优点:安全性高,因为状态信息保存在服务器端
      • 缺点:由于大型网站往往采用的是分布式服务器,浏览器发送的 HTTP 请求一般要先通过负载均衡器才能到达具体的后台服务器,倘若同一个浏览器两次 HTTP 请求分别落在不同的服务器上时,基于 Session 的方法就不能实现会话保持了
      • 解决办法:采用中间件,如redis,将session信息存储在redis中,使得每个服务器都可以访问到之前的信息
    • 基于 Cookie 实现的会话保持
      • 在服务器发送响应消息时,在 HTTP 响应头中设置 Set-Cookie 字段,用来存储客户端的状态信息。客户端解析出 HTTP 响应头中的字段信息,并根据其生命周期创建不同的 Cookie,这样一来每次浏览器发送 HTTP 请求的时候都会带上 Cookie 字段,从而实现状态保持。基于 Cookie 的会话保持与基于 Session 实现的会话保持最主要的区别是前者完全将会话状态信息存储在浏览器 Cookie 中
      • 优点:服务器不用保存状态信息,减轻服务器存储压力,同时便于服务端做水平拓展
      • 缺点:该方式不够安全,因为状态信息存储在客户端,这意味着不能在会话中保存机密数据。除此之外,浏览器每次发起 HTTP 请求时都需要发送额外的 Cookie 到服务器端,会占用更多带宽
      • 若遇到 Cookie 被禁用的情况,则可以通过重写 URL 的方式将会话标识放在 URL 的参数里,也可以实现会话保持
  • Q:HTTP/1.1 和 HTTP/1.0 的区别
    • 缓存处理:在HTTP/1.0中主要试用header里的if-modified-Since,Expires来做缓存判断的标准。而HTTP/1.1请求头中添加了更多与缓存相关的字段,从而支持更为灵活的缓存策略,例如Entity-tag, If-Unmodified-Since, If-Match, If-None-Match等可供选择的缓存头来控制缓存策略
    • 节约带宽:当客户端请求某个资源时,HTTP/1.0默认将该资源相关的整个对象传送给请求方,但很多时候可能客户端并不需要对象的所有信息。而在HTTP/1.1的请求头中引入了range头域,可使开发者可以多线程请求某一资源,从而充分的利用带宽资源,实现高效并发
    • 错误通知的管理:HTTP/1.1在1.0的基础上新增了24个错误状态响应码
    • Host请求头:早期HTTP/1.0中认为每台服务器都绑定一个唯一的IP地址并提供单一的服务,请求消息中的URL并没有传递主机名。而随着虚拟主机的出现,一台物理服务器上可以存在多个虚拟主机,并且它们共享同一个 IP 地址。为了支持虚拟主机,HTTP/1.1中添加了host请求头,请求消息和响应消息中应声明这个字段,若请求消息中缺少该字段时服务端会响应一个404错误状态码
    • 长连接:HTTP/1.0 默认浏览器和服务器之间保持短暂连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成后立即断开TCP连接。HTTP/1.1默认使用持久连接,支持在同一个TCP请求中传送多个HTTP请求和相应。在HTTP/1.1的请求头中,Connection字段帮助长连接的实现,当Connection请求头的值为Keep-alive时,客户端通知服务器返回本次请求结果后继续保持连接,而值为close时,服务器返回本次请求结果后会立即关闭连接
  • Q:HTTP1.X 和 HTTP2.0 的区别
    • 相比于HTTP/1.X的文本(字符串)传送,HTTP/2.0采用二进制传送。客户端和服务端传输数据时把数据分成帧,帧组成了数据流,流具有流ID标识和优先级,通过优先级以及流依赖能够一定程度上解决关键请求被阻塞的问题
    • HTTP/2.0支持多路复用。因为流ID的存在,通过同一个HTTP请求可以实现多个HTTP请求传输,客户端和服务端可以通过流ID来标识究竟是哪个流从而定位到是哪个HTTP请求
    • HTTP/2.0头部压缩。HTTP/2.0通过gzip和compress压缩头部然后再发送,同时通信双方会维护一张头信息表,所有字段都记录在这张表中,在每次HTTP传输时只需要传头字段在表中的索引即可,大大减小了重传次数和数据量
    • HTTP/2.0支持服务器推送。服务器在客户端未经请求许可的情况下,可预先向客户端推送需要的内容,客户端在退出服务时刻通过发送复位相关的请求来取消服务端的推送
  • Q:HTTP/2存在的问题
    • 统 Web 平台的数据传输都基于 TCP 协议,而 TCP 协议在创建连接之前不可避免的需要三次握手,如果需要提高数据交互的安全性,即增加传输层安全协议(TLS),还会增加更多的握手次数。HTTP从1.0到2.0,其传输层都是基于 TCP 协议的。即使是带来巨大性能提升的HTTP/2,也无法完全解决TCP协议存在的固有问题(慢启动,拥塞窗口尺寸的设置等)。此外,HTTP/2多路复用只是减少了连接数,其队头的阻塞问题并没有完全解决,倘若TCP丢包率过大,则HTTP/2的表现将不如HTTP/1.1
  • Q:QUIC协议
    • QUIC(Quick UDP Internet Connections),直译为快速UDP网络连接,是谷歌制定的一种基于UDP的低延迟传输协议。其主要目的是解决传输层TCP协议存在的问题,同时满足传输层和应用层对多连接、低延迟等的需求。该协议融合了TCP,TLS,HTTP/2等协议的特性,并基于UDP传输。该协议带来的主要提升有:
      • 低延迟连接。当客户端第一次连接服务器时,QUIC只需1 RTT(Round-Trip Time)延迟就可以建立安全可靠的连接(采用TLS1.3版本),相比TCP+TLS的3次RTT要更加快捷。之后,客户端可以在本地缓存加密的认证信息,当再次与服务器建立连接时可实现0 RTT的连接建立延迟
      • QUIC复用了HTTP/2协议的多路复用功能,由于 QUIC 基于 UDP,所以也避免了 HTTP/2存在的队头阻塞问题
      • 基于UDP协议的QUIC运行在用户域而不是系统内核,这使得QUIC协议可以快速的更新和部署,从而很好地解决了 TCP 协议部署及更新的困难
      • QUIC的报文是经过加密和认证的,除了少量的报文,其它所有的QUIC报文头部都经过了认证,报文主体经过了加密。只要有攻击者篡改QUIC报文,接收端都能及时发现
      • 具有向前纠错机制,每个数据包携带了除本身内容外的部分其它数据包的内容,使得出现少量丢包的情况下,尽量地减少其它包的重传次数,其通过牺牲单个包所携带的有效数据大小换来更少的重传次数,这在丢包数量较小的场景下能够带来一定程度的性能提升
  • Q:HTTP/3
    • HTTP/3是在QUIC基础上发展起来的,其底层使用UDP进行数据传输,上层仍然使用HTTP/2。在UDP与HTTP/2之间存在一个QUIC层,其中TLS加密过程在该层进行处理。HTTP/3 主要有以下几个特点:
    • 1.使用 UDP 作为传输层进行通信
    • 2.在 UDP 之上的 QUIC 协议保证了 HTTP/3 的安全性。QUIC 在建立连接的过程中就完成了 TLS 加密握手
    • 3.建立连接快,正常只需要 1 RTT 即可建立连接。如果有缓存之前的会话信息,则直接验证和建立连接,此过程 0 RTT。建立连接时,也可以带有少量业务数据
    • 4.不和具体底层连接绑定,QUIC 为每个连接的两端分别分配了一个唯一 ID,上层连接只认这对逻辑 ID。网络切换或者断连时,只需要继续发送数据包即可完成连接的建立
    • 5.使用QPACK进行头部压缩,因为在HTTP/2中HPACK要求传输过程有序,这会导致队头阻塞,而QPACK不存在这个问题
  • Q:怎么实现 DNS 劫持
    • DNS劫持即域名劫持,是通过将原域名对应的 IP 地址进行替换从而使得用户访问到错误的网站或者使得用户无法正常访问网站的一种攻击方式
    • 域名劫持往往只能在特定的网络范围内进行,范围外的DNS服务器能够返回正常的IP地址。攻击者可以冒充原域名所属机构,通过电子邮件的方式修改组织机构的域名注册信息,或者将域名转让给其它组织,并将新的域名信息保存在所指定的 DNS 服务器中,从而使得用户无法通过对原域名进行解析来访问目的网址
  • Q:socket() 套接字有哪些
    • 套接字是对网络中不同主机上的应用程序之间进行双向通信的端点的抽象,网络进程通信的一端就是一个套接字,不同主机上的进程是通过套接字来发送报文来进行通信
    • 有三种类型
    • 流套接字(SOCK_STREAM):基于TCP传输协议,主要用于提供面向连接、可靠的数据传输服务。由于TCP协议的特点,使用流套接字进行通信时能够保证数据无差错、无重复传送,并按顺序接收,通信双方不需要再程序中进行相应的处理
    • 数据报套接字(SOCK_DGRAM):基于UDP传输协议,对应于无连接的UDP服务应用。该服务并不能保证数据传输的可靠性,也无法保证对端能够顺序接收到数据。此外,通信两端不需建立长时间的连接关系,当UDP客户端发送一个数据给服务器后,可以通过同一个套接字给另一个服务器发送数据。当用UDP套接字时,丢包等问题需要在程序中进行处理
    • 原始套接字(SOCK_RAW):由于流套接字和数据报套接字只能读取 TCP 和 UDP 协议的数据,当需要传送非传输层数据包(例如ping命令时用的ICMP协议数据包)或者遇到操作系统无法处理的数据包时,此时需要建立原始套接字来发送
  • Q:网页解析全过程【用户输入网址到显示对应页面的全过程】
    • DNS解析
    • TCP连接
    • 发送HTTP请求,会涉及到负载均衡
    • 处理请求并返回
    • 浏览器渲染
    • 断开连接

第三部分:传输层

  • Q:三次握手
    • 是TCP连接的建立过程。在握手之前,主动打开连接的客户端结束CLOSE阶段,被动打开的服务器也结束CLOSE阶段,被动打开的服务器也结束CLOSE阶段,并进入LISTEN阶段。随后进入三次握手阶段
    • 1.首先客户端向服务器发送一个SYN包,并等待服务器确认,其中
      • 标志位为SYN,表示请求建立连接
      • 序号为Seq = x
      • 随后客户端进入SYN-SEND阶段
    • 2.服务器接收到客户端发来的SYN包后,对该包进行确认后结束LISTEN阶段,并返回一段TCP报文,其中
      • 标志位为SYN和ACK,表示确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接
      • 序号为Seq = y
      • 确认号为 Ack = x + 1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值,随后服务器端进入SYN-RCVD阶段
    • 3.客户端接收到发送的SYN+ACK包后,明确了从客户端到服务器的数据传输是正常的,从而结束SYN-SENT阶段。并返回最后一段报文。其中
      • 标志位为ACK,表示确认收到服务器端同意连接的信号
      • 序号为Seq = x + 1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值
      • 确认号为Ack = y + 1,表示收到的服务器端序号seq,并将其值加1作为自己的确认号Ack的值
      • 随后客户端将进入ESTABLISHED
    • 当服务器端收到来自客户端确认收到服务器数据的报文后,得知从服务器到客户端的数据传输时正常的,从而结束SYN-SEND阶段阶段,进入ESTABLISHED阶段,从而完成三次握手
  • Q:四次挥手
    • 即TCP连接的释放,这里假设客户端主动释放连接。在挥手之前主动释放连接的客户端结束ESTABLISHED。随后四次挥手
    • 1.首先客户端向服务器发送一段TCP报文想要释放TCP连接,其中
      • 标记位为FIN,表示请求释放连接
      • 序号为Seq = u
      • 随后客户端进入FIN-WAIT-1阶段,即半关闭阶段,并且停止向服务端发送通信数据
    • 2.服务器接收到客户端请求断开连接的FIN报文后,结束ESTABLISHED阶段,进入CLOSE-WAIT阶段并返回一段TCP报文,其中
      • 标记位位ACK,表示接收到客户端释放连接的请求
      • 序号为Seq = v
      • 确认号为Ack = u + 1,表示是在收到客户端报文的基础上,将其序号值加1作为本段报文确认号Ack的值
      • 随后服务器开始准备释放服务器端到客户端方向上的连接
    • 客户端收到服务器发送过来的TCP报文后,确认服务器已经收到了客户端连接释放的请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段
    • 3.服务器端在发出ACK确认报文后,服务器端会将遗留的待传数据传送给客户端,待传输完成后即经过CLOSE-WAIT阶段,便做好了释放服务器端到客户端的连接准备,再次向客户端发出一段TCP报文,其中
      • 标记位为FIN和ACK,表示已经准备好释放连接了
      • 序号为Seq = w
      • 确认号Ack = u + 1,表示是在收到客户端报文的基础上,将其序号Seq的值加1作为本段报文确认号Ack的值
    • 随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止向客户端发送数据
    • 4.客户端收到从服务器发来的TCP报文,确认了服务器已经做好释放连接的准备,于是结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器发送一段报文,其中
      • 标记位为ACK,表示接收到服务器准备好释放连接的信号
      • 序号为Seq = u+1,表示是在已收到服务器报文的基础上,将其确认号Ack值作为本段报文确认号的值
    • 随后客户端开始在TIME-WAIT阶段等待2MSL。服务器端收到从客户端发出的TCP报文结束LAST_ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段。由此完成四次挥手
  • Q:如果三次握手的时候每次握手信息对方没有收到会怎么样
    • 若第一次握手服务器未接收到客户端请求建立连接的数据包时,服务器不会进行任何相应的动作,而客户端由于在一段时间内没有收到服务器发来的确认报文,因此会等待一段时间后重新发送SYN同步报文,若仍然没有回应,则重复上述过程直到发送次数超过最大重传次数限制后,建立连接的系统调用会返回-1
    • 若第二次握手客户端未接收到服务器回应的ACK报文时,客户端会采取第一次握手失败时的动作,而服务器端此时将阻塞在accept()函数调用处等待client再次发送ACK报文
    • 若第三次握手服务器未接收到客户端发送过来的ACK报文,同样会采取类似于客户端的超时重传机制,若重传次数超过限制后仍然没有回应,则accept()系统调用返回-1,服务器端连接建立失败。但此时客户端认为自己已经连接成功了,因此开始向服务端发送数据,但是服务端的accept()系统调用已返回,此时没有在监听状态。因此服务器端接收到来自客户端发送来的数据时会发送RST报文给客户端,消除客户端单方面建立连接的状态
  • Q:为什么要进行三次握手?两次握手可以吗?
    • 三次握手的主要目的是确认自己和对方的发送和接收都是正常的,从而保证了双方能够进行可靠通信。若采用两次握手,当第二次握手后就建立连接的话,此时客户端知道服务器能够正常接收到自己发送的数据,而服务器并不知道客户端是否能够收到自己发送的数据
    • 网络往往是非理想状态的(存在丢包和延迟),当客户端发起创建连接的请求时,如果服务器直接创建了这个连接并返回包含SYN、ACK和Seq等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直接收不到返回的数据包。由于客户端可能设置了一个超时时间,一段时间后就关闭了连接建立的请求,再重新发起新的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器客户端能否收到服务器传输的数据的话,服务器端的端口会一直开着,等到客户端因超时重新发出请求时,服务器就好重新开启一个端口连接。长此以往, 这样的端口越来越多,就会造成服务器开销的浪费(虽说这话不完全正确)
  • Q:为什么要四次挥手?
    • 释放 TCP 连接时之所以需要四次挥手,是因为FIN释放连接报文和ACK确认接收报文是分别在两次握手中传输的。当主动方在数据传送结束后发出连接释放的通知,由于被动方可能还有必要的数据要处理,所以会先返回ACK确认报文。当被动方也没有数据再发送的时候,则发出连接释放通知,对方确认后才完全关闭TCP连接
  • Q:CLOSE-WAIT 和 TIME-WAIT 的状态和意义
    • 在服务器收到客户端关闭连接的请求并告诉客户端自己已经成功收到了该请求后,服务器进入了CLOSE-WAIT状态,然而此时服务端还有一些数据没有传输完成,因此不能立即关闭连接,而CLOSE-WAIT状态就是为了保证服务器在关闭连接之前将待发送的数据发送完成
    • TIME-WAIT发生在第四次挥手,当客户端向服务端发送ACK确认报文后进入该状态,若取消该状态,即客户端在收到服务端的FIN报文后立即关闭连接,此时服务端相应的端口并没有关闭,若客户端在相同的端口立即建立新的连接,则有可能接收到上一次连接中残留的数据包,可能会导致不可预料的异常出现。此外,假设客户端最后一次发送的ACK包在传输的时候丢失了,由于TCP协议的超时重传机制,服务端将重发FIN报文,若客户端并没有维持TIME-WAIT状态而直接关闭的话,当收到服务端重新发送的FIN包时,客户端就会用RST包来响应服务端,这将会使得对方认为是有错误发生,然而其实只是正常的关闭连接过程,并没有出现异常情况
  • Q:TIME_WAIT 状态会导致什么问题,怎么解决
    • 在高并发短连接的TCP服务器上,当服务器处理完请求后主动请求关闭连接,这样服务器上会有大量的连接处于TIME_WAIT状态,服务器维护每一个连接需要一个socket,也就是每个连接会占用一个文件描述符,而文件描述符的使用是有上限的,如果持续高并发,会导致一些正常的连接失败
    • 解决方法:修改配置或设置SO_REUSEADDR套接字,使得服务器处于TIME-WAIT状态下的端口能够快速回收和重用
  • Q:TIME-WAIT 为什么是 2MSL
    • 当客户端发出最后的ACK确认报文时,并不能确定服务端能够收到该段报文。所以客户端在发送完ACK确认报文后,会设置一个时长为2MSL的计时器。MSL(Maximum Segment Lifetime)指一段TCP报文在传输过程中的最大生命周期。2MSL即是服务端发出FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长
    • 若服务器在1MSL内没有收到客户端发出的ACK确认报文,再次向客户端发出FIN报文。如果客户端在2MSL内收到了服务器再次发来的FIN报文,说明服务器由于一些原因并没有收到客户端发出的ACK确认报文。客户端将再次向服务器发出ACK确认报文,并重新开始2MSL的计时
    • 若客户端在2MSL内没有再次收到服务器发送的FIN报文,则说明服务器正常接收到客户端ACK确认报文,客户端可以进入CLOSE阶段,即完成四次挥手
    • 所以客户端要经历2MSL时长的TIME-WAIT阶段,为的是确认服务器能否接收到客户端发出的ACK确认报文
  • Q:有很多 TIME-WAIT 状态如何解决
    • 服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口被占用,但TCP连接位于TIME_WAIT状态时可以重用端口。如果服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME-WAIT状态
    • 也可采用长连接方式减少TCP的连接与断开,在长连接的业务中往往不需要考虑TIME-WAIT状态,但其实在长连接的业务中并发量一般不会太高
  • Q:有很多 CLOSE-WAIT 怎么解决
    • 首先检查是不是自己的代码问题(看是否服务端程序忘记关闭连接),如果是,则修改代码
    • 调整系统参数,包括句柄相关参数和TCP/IP参数,一般一个CLOSE_WAIT会维持至少2个小时的时间,可以通过调整参数来缩短这个时间
  • Q:TCP 和 UDP 的区别
  • Q:TCP协议中的7种定时器
    • 建立连接定时器:该定时器是在建立 TCP 连接的时候使用的,在 TCP 三次握手的过程中,发送方发送 SYN 时,会启动一个定时器(默认为 3 秒),若 SYN 包丢失了,那么 3 秒以后会重新发送 SYN 包,直到达到重传次数
    • 重传定时器:该计时器主要用于 TCP 超时重传机制中,当TCP 发送报文段时,就会创建特定报文的重传计时器,并可能出现两种情况:
      • 1.若在计时器截止前发送方收到了接收方的ACK报文,则撤销该计时器
      • 2.若计时器截止时间内并没有收到接收方的 ACK 报文,则发送方重传报文,并将计时器复位
    • 坚持计时器:TCP通过让接收方指明希望从发送方接收的数据字节数(窗口大小)来进行流量控制,当接收端的接收窗口满时,接收端会告诉发送端此时窗口已满,请停止发送数据。此时发送端和接收端的窗口大小均为0,直到窗口变为非0时,接收端将发送一个确认ACK告诉发送端可以再次发送数据,但是该报文有可能在传输时丢失。若该ACK报文丢失,则双方可能会一直等待下去,为了避免这种死锁情况的发生,发送方使用一个坚持定时器来周期性地向接收方发送探测报文段,以查看接收方窗口是否变大
    • 延迟应答计时器:延迟应答也被称为捎带ACK,这个定时器是在延迟应答的时候使用的,为了提高网络传输 的效率,当服务器接收到客户端的数据后,不是立即回ACK给客户端,而是等一段时间,这样如果服务端有数据需要发送给客户端的话,就可以把数据和 ACK 一起发送给客户端了
    • 保活定时器:该定时器是在建立TCP连接时指定SO_KEEPLIVE时才会生效,当发送方和接收方长时间没有进行数据交互时,该定时器可以用于确定对端是否还活着
    • FIN_WAIT_2定时器:当主动请求关闭的一方发送FIN报文给接收端并且收到其对FIN的确认ACK后进入FIN_WAIT_2状态。如果这个时候因为网络突然断掉、被动关闭的一端宕机等原因,导致请求方没有收到接收方发来的FIN,主动关闭的一方会一直等待。该定时器的作用就是为了避免这种情况的发生。当该定时器超时的时候,请求关闭方将不再等待,直接释放连接
    • TIME_WAIT定时器:在 TCP 四次挥手中,发送方在最后一次挥手之后会进入 TIME_WAIT 状态,不直接进入CLOSE状态的主要原因是被动关闭方万一在超时时间内没有收到最后一个ACK,则会重发最后的FIN,2MSL(报文段最大生存时间)等待时间保证了重发的FIN会被主动关闭的一段收到且重新发送最后一个 ACK。还有一个原因是在这 2 MSL 的时间段内任何迟到的报文段会被接收方丢弃,从而防止老的 TCP 连接的包在新的 TCP 连接里面出现
  • Q:TCP 是如何保证可靠性的
    • 数据分块:应用数据被分割成 TCP 认为最适合发送的数据块
    • 序列号和确认应答:TCP给发送的每一个包进行编号,在传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答,即发送 ACK 报文,这个ACK报文当中带有对应的确认序列号,告诉发送方成功接收了哪些数据以及下一次的数据从哪里开始发。此外,接收方可以根据序列号对数据包进行排序,把有序数据传送给应用层,并丢弃重复的数据
    • 校验和:TCP将保持它首部和数据部分的校验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到报文段的检验和有差错,TCP 将丢弃这个报文段并且不确认收到此报文段。
    • 流量控制:TCP连接的双方都有一个固定大小的缓冲空间,发送方发送的数据量不能超过接收端缓冲区的大小。当接收方来不及处理发送方的数据,会提示发送方降低发送的速率,防止产生丢包。TCP通过滑动窗口协议来支持流量控制机制
    • 拥塞控制:当网络某个节点发生拥塞时,减少数据的发送
    • ARQ协议:为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组
    • 超时重传:当 TCP 发出一个报文段后,它启动一个定时器,等待目的端确认收到这个报文段。如果超过某个时间还没有收到确认,将重发这个报文段
  • Q:UDP 为什么是不可靠的?
    • UDP 只有一个 socket 接收缓冲区,没有 socket 发送缓冲区,即只要有数据就发,不管对方是否可以正确接收。而在对方的 socket 接收缓冲区满了之后,新来的数据报无法进入到 socket 接收缓冲区,此数据报就会被丢弃,因此 UDP 不能保证数据能够到达目的地,此外,UDP 也没有流量控制和重传机制,故UDP的数据传输是不可靠的
  • Q:bind 和 connect 对于 UDP 的作用是什么
    • UDP中调用connect只是把对端的IP和端口号记录下来,并且UDP可多次调用connect来指定一个新的IP和端口号,或断开旧的IP和端口号(通过设置connect函数的第二个参数)。和普通的 UDP 相比,调用 connect 的 UDP 会提升效率,并且在高并发服务中会增加系统稳定性
    • 当UDP的发送端调用bind函数时,就会将这个套接字指定一个端口,若不调用bind函数,系统内核会随机分配一个端口给该套接字。当手动绑定时,能够避免内核来执行这一操作,从而在一定程度上提高性能
  • Q:TCP 的停止等待协议是什么
    • 停止等待协议是为了实现 TCP 可靠传输而提出的一种相对简单的协议,该协议指的是发送方每发完一组数据后,直到收到接收方的确认信号才继续发送下一组数据。通过四种情形来帮助理解停等协议是如何实现可靠传输的:
    • 1.无差错传输
      • A 发送分组 Msg 1,发完就暂停发送,直到收到接收方确认收到 Msg 1 的报文后,继续发送 Msg 2,以此类推,该情形是通信中的一种理想状态
    • 2.出现差错
      • 发送方发送的报文出现差错导致接收方不能正确接收数据,出现差错的情况主要分为两种:
        • 发送方发送的 Msg 1 在中途丢失了,接收方完全没收到数据。
        • 接收方收到 Msg 1 后检测出现了差错,直接丢弃 Msg 1。
      • 上面两种情形,接收方都不会回任何消息给发送方,此时就会触发超时传输机制,即发送方在等待一段时间后仍然没有收到接收方的确认,就认为刚才发送的数据丢失了,因此重传前面发送过的数据
    • 3.确认丢失
      • 当接收方回应的 Msg 1 确认报文在传输过程中丢失,发送方无法接收到确认报文。于是发送方等待一段时间后重传 Msg 1,接收方将收到重复的 Msg1 数据包,此时接收方会丢弃掉这个重复报文并向发送方再次发送 Msg1 的确认报文
    • 4.确认迟到
      • 当接收方回应的 Msg 1 确认报文由于网络各种原因导致发送方没有及时收到,此时发送方在超时重传机制的作用下再次发送了 Msg 数据包,接收方此时进行和确认丢失情形下相同的动作(丢弃重复的数据包并再次发送 Msg 1 确认报文)。发送方此时收到了接收方的确认数据包,于是继续进行数据发送。过了一段时间后,发送方收到了迟到的 Msg 1 确认包会直接丢弃
  • Q:TCP 最大连接数限制
    • Client 最大 TCP 连接数
      • client 在每次发起 TCP 连接请求时,如果自己并不指定端口的话,系统会随机选择一个本地端口(local port),该端口是独占的,不能和其他 TCP 连接共享。TCP 端口的数据类型是 unsigned short,因此本地端口个数最大只有 65536,除了端口 0不能使用外,其他端口在空闲时都可以正常使用,这样可用端口最多有 65535 个
    • Server 最大 TCP 连接数
      • Server 通常固定在某个本地端口上监听,等待 client 的连接请求。不考虑地址重用(Unix 的 SO_REUSEADDR选项)的情况下,即使server端有多个IP,本地监听端口也是独占的,因此server端TCP连接4元组中只有客户端的IP地址和端口号是可变的,因此最大TCP连接为客户端IP数 x 客户端port数,对IPv4,在不考虑IP地址分类的情况下,最大TCP连接数约为2的32次方(IP数)x2的16次方(port数),即server端单机最大TCP连接数约为2的48次方
      • 上面给出的是只是理论上的单机最大连接数,在实际环境中,受到明文规定(一些 IP 地址和端口具有特殊含义,没有对外开放)、机器资源、操作系统等的限制,特别是 sever 端,其最大并发 TCP 连接数远不能达到理论上限。对 server 端,通过增加内存、修改最大文件描述符个数等参数,单机最大并发 TCP 连接数超过 10 万 是没问题的
  • Q:TCP 流量控制与拥塞控制
    • 流量控制
      • 所谓流量控制就是让发送方的发送速率不要太快,让接收方来得及接收。如果接收方来不及接收发送方发送的数据,那么就会有分组丢失。在 TCP 中利用可边长的滑动窗口机制可以很方便的在 TCP 连接上实现对发送方的流量控制。主要的方式是接收方返回的 ACK 中会包含自己的接收窗口大小,以控制发送方此次发送的数据量大小(发送窗口大小)
    • 拥塞控制
      • 在实际的网络通信系统中,除了发送方和接收方外,还有路由器,交换机等复杂的网络传输线路,此时就需要拥塞控制。拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况。常用的解决方法有:慢开始和拥塞避免、快重传和快恢复
    • 拥塞控制和流量控制的区别
      • 拥塞控制往往是一种全局的,防止过多的数据注入到网络之中,而TCP连接的端点只要不能收到对方的确认信息,猜想在网络中发生了拥塞,但并不知道发生在何处,因此,流量控制往往指点对点通信量的控制,是端到端的问题
  • Q:如果接收方滑动窗口满了,发送方会怎么做
    • 基于 TCP 流量控制中的滑动窗口协议,接收方返回给发送方的 ACK 包中会包含自己的接收窗口大小,若接收窗口已满,此时接收方返回给发送方的接收窗口大小为 0,此时发送方会等待接收方发送的窗口大小直到变为非 0 为止,然而,接收方回应的 ACK 包是存在丢失的可能的,为了防止双方一直等待而出现死锁情况,此时就需要坚持计时器来辅助发送方周期性地向接收方查询,以便发现窗口是否变大,当发现窗口大小变为非零时,发送方便继续发送数据
  • Q:TCP拥塞控制采用的4种算法
    • 慢开始
      • 当发送方开始发送数据时,由于一开始不知道网络负荷情况,如果立即将大量的数据字节传输到网络中,那么就有可能引起网络拥塞。一个较好的方法是在一开始发送少量的数据先探测一下网络状况,即由小到大的增大发送窗口(拥塞窗口 cwnd)。慢开始的慢指的是初始时令 cwnd为 1,即一开始发送一个报文段。如果收到确认,则 cwnd = 2,之后每收到一个确认报文,就令 cwnd = cwnd* 2。
      • 但是,为了防止拥塞窗口增长过大而引起网络拥塞,另外设置了一个慢开始门限 ssthresh。
      • 1.当 cwnd < ssthresh 时,使用上述的慢开始算法;
      • 2.当 cwnd > ssthresh 时,停止使用慢开始,转而使用拥塞避免算法;
      • 3.当 cwnd == ssthresh 时,两者均可。
    • 拥塞避免
      • 拥塞控制是为了让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT (往返时间定义为发送方发送数据到收到确认报文所经历的时间)就把发送方的 cwnd 值加 1,通过让 cwnd 线性增长,防止很快就遇到网络拥塞状态。
      • 当网络拥塞发生时,让新的慢开始门限值变为发生拥塞时候的值的一半,并将拥塞窗口置为 1 ,然后再次重复两种算法(慢开始和拥塞避免),这时一瞬间会将网络中的数据量大量降低。
    • 快重传
      • 快重传算法要求接收方每收到一个失序的报文就立即发送重复确认,而不要等到自己发送数据时才捎带进行确认,假定发送方发送了 Msg 1 ~ Msg 4 这 4 个报文,已知接收方收到了 Msg 1,Msg 3 和 Msg 4 报文,此时因为接收到收到了失序的数据包,按照快重传的约定,接收方应立即向发送方发送 Msg 1 的重复确认。 于是在接收方收到 Msg 4 报文的时候,向发送方发送的仍然是 Msg 1 的重复确认。这样,发送方就收到了 3 次 Msg 1 的重复确认,于是立即重传对方未收到的 Msg 报文。由于发送方尽早重传未被确认的报文段,因此,快重传算法可以提高网络的吞吐量。
    • 快恢复
      • 快恢复算法是和快重传算法配合使用的,该算法主要有以下两个要点:
      • 1.当发送方连续收到三个重复确认,执行乘法减小,慢开始门限 ssthresh 值减半;
      • 2.由于发送方可能认为网络现在没有拥塞,因此与慢开始不同,把 cwnd 值设置为 ssthresh 减半之后的值,然后执行拥塞避免算法,线性增大 cwnd
  • Q:TCP 粘包问题
    • 发生TCP粘包和拆包的原因
      • 1.发送方写入的数据大于套接字缓冲区的大小,此时将发生拆包
      • 2.发送方写入的数据小于套接字缓冲区大小,由于TCP默认使用Nagle算法,只有当收到一个确认后,才将分组发送给对端,当发送方收集了多个较小的分组,就会一起发送给对端,这将会发生粘包
      • 3.进行 MSS (最大报文长度)大小的 TCP 分段,当 TCP 报文的数据部分大于 MSS 的时候将发生拆包
      • 4.发送方发送的数据太快,接收方处理数据的速度赶不上发送端的速度,将发生粘包
    • 常见解决方法
      • 1.在消息的头部添加消息长度字段,服务端获取消息头的时候解析消息长度,然后向后读取相应长度的内容
      • 2.固定消息数据的长度,服务端每次读取既定长度的内容作为一条完整消息,当消息不够长时,空位补上固定字符。但该方法会浪费网络资源
      • 3.设置消息边界,也可理解为分隔符,服务端从数据流中按消息边界分理出消息内容,一般使用换行符
    • 处理粘包问题的时机
      • 当接收端同时收到多个分组,并且这些分组之间毫无关系时,需要处理粘包
      • 而当多个分组属于同一数据的不同部分时,并不需要处理粘包问题
  • Q:SYN FLOOD是什么
    • 是典型的DoS(拒绝服务)攻击,其目的是通过消耗服务器所有可用资源使服务器无法用于处理合法请求。通过重复发送初始连接请求(SYN)数据包,攻击者能够压倒目标服务器上的所有可用端口,导致目标设备根本不响应合法请求

第四部分:网络层

  • Q:两台电脑连起来后ping不通的可能问题
    • 1.看网络是否连接正常,检查网卡驱动是否正确安装
    • 2.局域网设置问题,检查 IP 地址是否设置正确
    • 3.看是否被防火墙阻拦(有些设置中防火墙会对 ICMP 报文进行过滤),如果是的话,尝试关闭防火墙
    • 4.看是否被第三方软件拦截
    • 5.两台设备间的网络延迟是否过大(例如路由设置不合理),导致 ICMP 报文无法在规定的时间内收到

参考文献

Last Updated:
Contributors: Shiqi Lu