TCP头部信息
- 关键信息:
- 32位序列号:每个包中表示当前这个数据包的唯一值
- 32为确认序列号:假如本次序列号是k+1, 目的主机返回的确认号就是告诉对方,我已经收到了你之前发送的序列号为k-1的消息
- ACK 标志:表示确认是否有效,带上ACK表示当前消息是确认报文段
- FIN:表示关闭请求的标志位
- SYN:表示建立请求的标示位
TCP连接建立
- 首先客户端,服务器TCP进程都处于关闭状态
- TCP服务器是被动等待的,因此服务端会进入监听状态
- 客户端发送链接请求,链接请求标志位 SYN = 1 ,当前报文的32位序列号 seq = x,此时客户端处于同步发送状态
- 服务端收到请求,回复客户端 SYN = 1, seq = y,ACK = 1,ack = x+1,服务端处于同步接收状态
- 客户端收到后回复服务端 ACK = 1,seq = x+1 ack = y+1,此时双方处于已经链接状态
如果只有2次会出现什么问题
- 因为TCP链接发送过程依赖网络的节点是否正常,当发送没有到达Server,Client没收到回复会重试发送链接
- 此时就有这个问题,当第一次链接请求被某个路由器阻塞,导致重发,第二次重发成功并链接后完成数据传输后,双方关闭,Server重新会监听状态
- 此时被路由器阻塞的恢复,该TCP链接请求到Server,导致server端处于同步链接状态,并且会一直重试发送ACK回复信息给Client。因此此时client已经关闭,导致Server端资源浪费
TCP三次握手为什么是3次,2次,4次 是否可以
- TCP是面向连接的,也就是在数据传输之前需要建立面向双方(客户端,服务端)的连接
- 2次的异常情况二:
- 假如只是做两次握手,我们无法确认客户端的接收能力,因为客户端发送syn,服务端回复后,只能确保服务器准备好了连接,此时可服务端是建立连接状态。
- 如果此时客户端并不少正常连接而是一个黑客或者恶意请求信息,我发完连接请求后不给任何新的数据信息请求,这就导致服务端可能一直处于连接状态而无法接受数据,导致资源浪费,如果这种恶意请求巨大,会导致服务器为这种恶意请求建立过多的连接而导致正常请求的客户端无法建立连接
- 非4次的原因:
- 因为TCP三次握手就已经能够确认客户端和服务器端都已经准备好建立连接,无需第四次浪费资源
TCP链接关闭
- 由客户端主动发起链接关闭,客户端发送链接关闭请求 FIN = 1,seq = u,ack = v,ACK = 1
- 此处FIN是关闭标志位,seq当前请求头中序列号,ack 表示上次接收到的server的请求的seq +1,并且表示ACK是请求有效
- client处于等待状态
- 服务队收到后,会先回复一个收到的确认回复, ACK = 1 , seq = v, ack = u+1
- 此时客户端处于等待状态2 ,他需要等待服务的有可能被阻塞住的请求信息
- 服务的等候一段时间后发送关闭确认, FIN = 1 ,ACK = 1, seq = w, ack = u+1
- 最后客户端发送消息接收确认: ACK = 1,seq = u+1,ack = w+1
- 此时客户端还会在等待一段时间后才会关闭链接,确保最后的ACK回复是一定能到达的。
TCP四次挥手是否可以是3次,什么时候是3次?
- 在正常有数据传输的情况下只能是四次,能合并的是第二次ACK 和第三次 FIN确认
- 在有数据传输的时候,因为数据传输是依赖网络环境的,如果收到客户端的FIN后,服务器处于CLOSE-WAIT状态,等待还在传输的数据的到来,
- 如果此时不给一个ACK,那么几秒后,client端口会认为FIN请求丢失或者异常没有到达服务的,client会不断的请求造成Client的资源的浪费
- 因此service必须要先ACK确认收到FIN请求防止client端的重试,让后等数据都接受到在发送确认FIN,由此可见有数据传输情况这两次必不可少并且一定要分开
- 而第四次同样也是,需要等Client的ACK回复,要不然Service依然会不断重试
- 3次挥手关闭情况:如果第二次与第三次挥手之间没有数据发送,那么被动断开连接的一方就可能会把第二次的 ACK 与 第三次的 FIN 合并为一次挥手
TCP 可靠传输实现
- 分段传输:应用数据会被分割成TCP认为的合适大小发送
- 超时重传:当TCP发出一个报文,他会启动一个定时任务,如果不能在定时任务指定时间收到回复则重新发送
- CRC校验:
- 流量控制
- 滑动窗口:
- 应答机制:TCP的应答机制用的是累计应答,接收方在接收到多个数据包后在根据接收到的数据包应答
- 窗口:窗口的实现是依赖操作系统开辟的一个缓存空间,也就无需等待应答而可以继续发送数据的一个缓冲区,发送方在收到应答之前需要将发送的数据显缓存在缓冲区。
- 滑动窗口:还是通过缓冲区实现,当发现缓冲区的部分数据已经是确认的,那么将发送窗口的缓冲区向没有确认这边移动,一直到第一个没有确认的数据为准
- 例如 一个窗口能容纳的缓冲数据量是200,假如现在已经发送了70,接收方收到了70并且通知了server我现在只能在收130 了,下次发130以内
- 那么 接收方先将70 处理完,让后窗口向右边移动70(清空已经处理的70) ,又收到了130,那么此时他会回复 我可以接受70.
- 以此循环往复
为什么SYN/FIN不包含数据却要消耗一个序列号
- 凡事需要对端确认的情况,一定会消耗TCP报文序列号,因为对端确认的时候回复的就是 序列号+1
- SYN 和 FIN 都需要对端确认,所以一定需要消耗一个序列号。
什么是半连接队列,什么事SYN Flood攻击
- 客户端大量伪造IP发送SYN包,服务端会回复ACK + SYN去这个未知的IP地址,必然得不到回复ACK信息,导致服务器这个线程一直处于SYN_RCVD(同步已发送)状态不断重试ACK+SYN,而服务器中的线程池队列大小事有限的,如果半连接队列满了,也会无法完成对正常请求的一个回复。
TCP快速打开(TFO)原理
- TCP快速打开做法是,第一次带获取Cookie,第二次通过Cookie校验:
- 在第一次TCP三次握手的时候,去server端请求一个Cookie信息,保存在client’端
- 本次建立连接,发送数据,四次挥手,断开连接,Cookie还在Client本地
- 第二次发现又需要发送数据,那么client端会在SYN中带上上次的Cookie,并且加上本次的HttpGet请求信息
- server端口校验client端口SYN中的Cookiei信息通过后,给一个SYN+ACK,同时在发一次HttpResponse,
- 至此TFO完成,也就是在SYN确认的同时完成了一次数据传输
- 优点:少了一次Client端对Server端的请求,就少了一次往返RTT
TCP报文中时间戳作用
- TCK时间戳由四部分组成:类别 ,长度,发送方时间戳(TS value),回显时间戳(TS Echo Reply)
- TCP时间戳主要解决两个问题:
- 第一个问题,计算往返延迟时间RTT,因为在ACK包中包含了TSval 和TSecr,这样无论是正常包,还是重传确认包,都可以通过这两个值计算出RTT
- 第二个问题序列号回绕问题:序列号回绕意思是232的数据用完了,我从头开始用,也就是序列号存在重复的问题:假设第一个223流程中,第2个包阻塞,等到第二个232的流程中的第二个包发送时候,第一个流程中阻塞的包正好一起到Server,那么这两个包的Seq将会是一样的,这样我们可以通过报文中时间戳来做区分。
TCP超时重传时间计算,超时多久开始重传
- 时间设置不合理会出现两个问题:
- 太早重传,可能导致不必要重传,因为很有可能ACK已经在发送回来的路上
- 台晚重传,可能导致丢包很久才重传
- 计算方式:
- 简单做法,取出平均值,比如,第一次RTT 500ms,第二次RTT 800ms,那么第三次时候取平均值RTO 650ms
- 经典算法“平滑往返时间”:在得出一个RTT后,每来一次请求,更新一次RTT值,如下公式,也就是用80%当前RTT + 20%的新RTT当 最新的RTT
TCP流量控制
- 对发送端 与 接收端,TCP需要吧发送数据放到发送缓冲区,将接收数据页放到接收缓冲区
- 流量控制就是通过缓冲区的大小来控制发送端的数据发送量。如果对方接收缓冲区满就不在继续发送
- 为控制流量 client端会在ACK中带上我当前接收缓冲区还剩多大,Server端就通过这个来调整下次发送数据的大小。
如何理解TCP的Keep-alive原理
- 当一个Web应用,Client端在三次握手后,故障宕机,对于Server是无法感知的,那么下一个数据包将无法到达。
- TCP协议为了考虑这种情况设计来一种监测机制,就是Keepalive,作用就是探测对端的连接有没有失效,通过定时发送探测包来检查对方是否还存活,不过默认要7200s没有数据包才会发送keepalive探测包,这个时间对于普通业务系统太长,所以我们都是通过自己在应用层来实现心跳
TCP场景问题
- AB两个主机建立了一个TCP连接,A向B发送两个TCP报文,大小分别500,300,第一个报文的序列号是200,那么B主机这两个报文需要返回的序列号是多少?
- 第一个报文回复确认序列号 200+500 = 700
- 第二个报文回复确认序列号 700+300 = 1000
TCP中端口号
- TCP头部信息中用两个字节的整数来表示端口号,并且存在 源头端口号(Server port) 目标端口号(Destination port),比如服务器要请求百度,那么需要先生成一个临时端口号XXX作为 源端口号,目标端口号就是80
- 因为是2字节16位,因此端口号能表示的范围是 216 = 65536个端口号
- 端口号有如下分类:
- 约定俗成的端口号:0~1023 ,例如http的80,https的443,ssh对应的22
- 已经登机过的端口号:1024 ~ 491151 ,例如mysql的3306, redis的6379 等
- 临时端口号:49152 ~ 65535
TCP问题二
- TCP提供了字节流服务,而收发双方都不保持边界,应用程序如何提供他们自己的记录标识
- 应用程序自己约定规则来表示消息边界(同时约定序列化方法),比如一些使用回车+换行(\r\n),比如redis就是这样的通讯协议
Telnet的用法
- Telnet 的一个最大作用就是检查一个端口是否处于打开状态,使用的命令是telnet 127.0.0.1 8989,这条命了能监测远端Server 指定端口的网络连接是否可达。
TCP 和UDP的区别
- TCP是面向连接的,可靠的,基于字节流的传输层协议
- UDP是一个面向无连接的传输层协议
- 面向连接是指:在双方建立通讯之前,TCP需要经过三次握手建立连接,而UDP没有相应建立连接的过程。
- 可靠性:
- TCP有状态:TCP会记录哪些数据发送了,通过seq,哪些数据接收了,哪些没有接收(buffer中的没有接收完),而保证数据安顺序到达,不允许有差错
- TCP可控:TCP再有丢包,获取网络不可达导致收到不ACK时候,会更具具体情况调整发送行为,例如会重试发送,会减少发送包大小等。
UDP协议
- UDP协议是无连接的,不可靠的传输层协议(不需要三次握手,四次挥手)
- UDP不提供数据包分组,组装,不能对数据包排序,报文发送后无法得知是否到达。
- UDP适合实时的数据传输,例如IP电话,网络视频会议等
- UDP应用:
- UDP至此单播,多播,广播
设计一个QQ,在网络协议上怎么设计
- 登陆采用TCP协议和HTTP协议,和好友之间发送消息采用UDP协议,内网文件采用P2P技术
- 总的来说:
- 登陆过程:客户端Client采用TCP协议向server发送信息,HTTP协议下载新信息,登陆后,维持一个TCP长连接维持在线状态
- 消息发送:客户端Client采用UDP协议,但是需要通过服务器转发。腾讯为保证可靠性,采用上层协议保证可靠性。如果发送失败,客户端提示失败,用户触发重试
- 文件传输:如果是内网两个客户端之间传输文件,QQ采用P2P技术,不需要服务器中转。
TCP抓包工具
- wireShark TCP抓包工具
- ELK(elasticSearch Logstash Kibana) 体系下的packetbeat[2] 插件
Https
- TCP三次握手
- 服务端返回数字证书(公钥)
- client 端生成随机密码 & 用公钥给随机密码加密
- client 将加密后的随机密码发给server端
- client发送加密后的请求包
- server端利用收到的加密的随机密码 & 公钥 给密码解密,得到随机密码让后给数据包解密