TCP协议概述
- 属于 传输层通信协议
- 基于TCP的应用层协议有HTTP、SMTP、FTP、Telnet 和 POP3
主要特点:面向连接、面向字节流、全双工通信、通信可靠。
优缺点:
- 优点:数据传输可靠
- 缺点:效率慢(因需建立连接、发送确认包等)
应用场景:要求通信数据可靠时,即 数据要准确无误地传递给对方。如:传输文件:HTTP、HTTPS、FTP等协议;传输邮件:POP、SMTP等协议
- 万维网:HTTP协议
文件传输:FTP协议
电子邮件:SMTP协议
远程终端接入:TELNET协议
简单了解TCP报文格式
- TCP虽面向字节流,但传送的数据单元 = 报文段
- 报文段 = 首部 + 数据 2部分
- TCP的全部功能体现在它首部中各字段的作用,故下面主要讲解TCP报文段的首部
ps:首部的前 20 个字节固定,后面有 4n 字节根据需要增加。故 TCP首部最小长度 = 20字节(最大60个字节)。
- 端口号:用来标识同一计算机的不同的应用进程。
- 源端口:源端口与ip地址的作用是标识报文的返回地址
- 目的端口:目的端口指明接收方计算机上的应用程序
TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。
- 序号与确认号:是TCP可靠传输的关键部分。序号是本报文段发送的数据组的第一个字节的序号。在TCP传送的流中,每一个字节一个序号。e.g.一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。确认号,即ACK,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。
- 数据偏移/首部长度:4bits。由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值。
- 保留:为将来定义新的用途保留,现在一般置0。
- 控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。
- URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针。
- ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。
- PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
- RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求。例如,A向B发起连接,但B之上并未监听相应的端口,这时B操作系统上的TCP处理程序会发RST包。
- SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。
- FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。
- 窗口字段:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小时一个16bit字段,因而窗口大小最大为65535。
- 奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。
- 紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。
- 选项和填充:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。
- 数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
重要字段:
ACK与ack的区别
客户端与服务器来回共发送三个TCP报文段来建立运输连接,三个TCP报文段分别为:
(1)客户端A向服务器B发送的TCP请求报段“SYN=1,seq=x”;
(2)服务器B向客户端A发送的TCP确认报文段“SYN=1,ACK=1,seq=y,ack=x+1”;
(3)客户端A向服务器B发送的TCP确认报文段“ACK=1,seq=x+1,ack=y+1”。
- ACK:这里出现的ACK即为上面所说的TCP报文段首部中的“ACK字段”,置1时该报文段为确认报文段。
- ack:而ack则为TCP报文段首部中“确认号字段”的具体数值(序列号)。ack=x+1说明B希望A下次发来的报文段的第一个数据字节为序号=x+1的字节;ack=y+1说明A希望B下次发来的报文段的第一个数据字节为序号=y+1的字节。
建立连接过程
ps:在建立TCP连接之前,客户端和服务器都处于关闭状态(CLOSED),直到客户端主动打开连接,服务器才被动打开连接(处于监听状态 = LISTEN),等待客户端的请求。
为什么一定要进行三次握手?两次握手或者四次握手不可以呢?
TCP 协议是一个面向连接的、安全可靠的传输层协议,三次握手的机制是为了保证能建立一个安全可靠的连接。
- 第一次握手:客户端向服务器发送一个连接请求的报文段,在报文里面:SYN标志位置为1,同时随机选择初始化序列号seq = x,客户端进入SYN-SENT(同步已发送)状态,即等待服务器确认。此时客户端什么都不能确认,服务器确认了对方发送正常,自己接收正常;
- 第二次握手:当服务器收到这个报文段之后,若同意建立连接(为该TCP连接分配TCP缓存、变量),就向客户端发送一个确认报文段,在确认报文段中SYN 位和 ACK 位都置 1,确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y, 服务器进程进入 SYN-RCVD(同步已收到)状态。此时客户端确认了自己和对方的收发正常;服务器确认了对方发送正常,自己接收正常;
注意:上述两次握手不能携带数据,但要消耗一个序列号。 - 第三次握手:当客户端收到服务器发送的确认响应报文之后(为该TCP连接分配TCP缓存、变量),还要继续再次给服务器发送连接确认报文段,确认报文段的 ACK 置 1,确认号 ack = y + 1,而自己的序号 seq = x + 1,客户端和服务器都进入ESTABLISHED(已建立连接)状态。
注意:这时 ACK 报文段可以携带数据。但如果不携带数据则不消耗序号,这种情况下,下一个数据报文段的序号仍是 seq = x + 1。
通过上述三次握手,双方确认自己与对方的发送与接收是正常的,就建立起一条TCP连接,即可传送应用层数据。ps:因 TCP提供的是全双工通信,故通信双方的应用进程在任何时候都能发送数据;三次握手期间,任何1次未收到对面的回复,则都会重发。
为什么两次握手不行呢?
结论:防止服务器接收了早已经失效的连接请求报文,服务器同意连接,从而一直等待客户端请求,最终导致形成死锁、浪费资源。
ps:SYN洪泛攻击:(具体见下文)
- 从上可看出:服务端的TCP资源(TCP缓存、变量)分配时刻 = 完成第二次握手时;而客户端的TCP资源分配时刻 = 完成第三次握手时。
- 这就使得服务器易于受到SYN洪泛攻击,即同时多个客户端发起连接请求,从而需进行多个请求的TCP连接资源分配。
为什么不需要四次握手呢?
- 有人可能会说 A 发出第三次握手的信息后在没有接收到 B 的请求就已经进入了连接状态,那如果 A 的这个确认包丢失或者滞留了怎么办?我们需要明白一点,完全可靠的通信协议是不存在的。在经过三次握手之后,客户端和服务器已经可以确认之前的通信状况,都收到了确认信息。所以即便再增加握手次数也不能保证后面的通信完全可靠,所以是没有必要的。
第2次握手传回了SYN,为什么还要传回ACK?
- 回传了SYN(同步序号标志)只是证明服务器收到的确实是客户端发送的信号,证明从客户端到服务器的通信是正常的。**
- 但是服务器到客户端之间的通道还需要ACK(确认序号标志)来保证信息的准确无误。
SYN 同步序列编号(Synchronize Sequence Numbers) 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK确认序号标志消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。
什么是SYN攻击(半连接攻击)?
- 半连接攻击是一种攻击协议栈的攻击方式,坦白说就是攻击主机的一种攻击方式。通过将主机的资源消耗殆尽,从而导致应用层的程序无资源可用,导致无法运行。
正常情况下,三次握手过程中,服务器发送 SYN-ACK 之后,收到客户端的 ACK 之前的 TCP 连接称为半连接(half-open connect)。此时服务器处于 SYN_RCVD 状态。当收到 ACK 后,服务器才能转入 ESTABLISHED 状态。
SYN 攻击指的是,攻击客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送SYN包,服务器回复确认包,将收到SYN包放入半连接队列,并等待客端户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,导致目标系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。
如何来解决半连接攻击?
- 可以通过拓展半连接队列的大小,来进行补救,但缺点是,不能无限制的增加,这样会耗费过多的服务端资源,导致服务端性能地下。这种方式几乎不可取。
- 现主要通syncookies或者syn中继机制来防范半连接攻击,不为半连接分配核心内存的方式来防范。syncookies 的原理就是当服务端收到客户端 SYN 包后,不会放到半连接队列里,而是通过 {src_ip, src_port, timestamp} 等计算一个 cookie(也就是一个哈希值),通过 SYN+ACK包返回给客户端,客户端返回一个 ACK 包,携带上这个 cookie,服务端通过校验可以直接把这个连接放入全连接队列。整个过程不需要半连接队列的参与。
tcp全连接攻击?
- 全连接攻击是通过消耗服务端进程数和连接数,只连接而不进行发送数据的一种攻击方式。当客户端连接到服务端,仅仅只是连接,此时服务端会为每一个连接创建一个进程来处理客户端发送的数据。但是客户端只是连接而不发送数据,此时服务端会一直阻塞在recv或者read的状态,如此一来,多个连接,服务端的每个连接都是出于阻塞状态从而导致服务端的崩溃。
如何来解决全连接攻击?
- 可以通过不为全连接分配进程处理的方式来防范全连接攻击,具体的情况是当收到数据之后,在为其分配一个处理线程。具体的处理方式在accept返回之前是不分配处理线程的。直到接收相关的数据之后才为之提供一个处理过程。
例如在apache服务中,是通过预创建一定量的子进程作为处理连接继承。所有的自己进程都继承父进程的sockfd,每当有一个连接过来时,只有当accept返回是,才会为该链接分配一个进程来处理连接请求。负责,子进程一直处于等待状态。如果出现值是连接存在,而始终不放数据,该链接的状态是SYN_RECV,在协议栈中,提供一个保活期给该链接,如果超过保活期还没有数据到来,服务端协议栈将会断开该链接。如果没有该保活期,虽然避免了ESTABLESHED状态的数量,但是SYN_RECV的数据量的增长仍旧是不可估算的,所以需要利用保活期来监控该链接是需要清除断开。
释放连接的过程
为什么断开一个 TCP 连接则需要“四次挥手”?
- 第一次挥手:客户端服务器发送一个连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。在这段报文段中,终止控制位FIN 置 1 ,其序号 seq = u(等于前面已传送过的数据的最后一个字节的序号加 1),这时客户端进入 FIN-WAIT-1(终止等待1)状态,等待服务器的确认。
- 第二次挥手:服务器收到连接释放报文段后立即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是 v(等于服务器前面已经传送过的数据的最后一个字节的序号加1),然后服务器就进入 CLOSE-WAIT(关闭等待)状态,客户端进入 FIN-WAIT-2(终止等待2)状态。
- 第三次挥手:若服务器已经没有要向客户端发送的数据,其应用进程就通知 TCP 释放连接。这时服务器发出的连接释放报文段必须使 FIN = 1。假定服务器的序号为 w(在半关闭状态,服务器可能又发送了一些数据)。服务器 还必须重复上次已发送过的确认号 ack = u + 1。这时服务器就进入 LAST-ACK(最后确认)状态,等待客户端的确认。
- 第四次挥手:客户端在收到服务器的连接释放报文后,必须对此发出确认。在确认报文段中把 ACK 置 1,确认号 ack = w + 1,而自己的序号 seq = u + 1(前面发送的 FIN 报文段要消耗一个序号)。然后进入 客户端TIME-WAIT(时间等待) 状态。
请注意,现在 TCP 连接还没有释放掉。必须经过时间等待计时器设置的时间 2MSL(MSL:最长报文段寿命)后,客户端才能进入到 CLOSED 状态,然后撤销传输控制块,结束这次 TCP 连接。当然如果服务器一收到 客户端的确认就进入 CLOSED 状态,然后撤销传输控制块。所以在释放连接时,服务器结束 TCP 连接的时间要早于客户端。
TCP是全双工的连接,必须两端同时关闭连接,连接才算真正关闭。简言之,客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器才会发送 FIN 连接释放报文,对方确认后就完全关闭了TCP连接。
举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
为什么 TIME-WAIT 状态必须等待 2MSL 的时间呢?
- 原因1:为了保证客户端发送的最后1个连接释放确认报文 能到达服务器,从而使得服务器能正常释放连接。等待 2MSL 用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
- 原因2:防止已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种早已经失效的连接请求报文段。
ps:设想这样一个情景:客户端已主动与服务器建立了 TCP 连接。但后来客户端的主机突然发生故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用TCP的保活计时器。基本原理:
- 服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两个小时。若两个小时都没有收到客户端的数据,服务器就发送一个探测报文段,以后则每隔 75 秒钟发送一次。若连续发送 10个 探测报文段后仍然无客户端的响应,服务器就认为客户端出了故障,接着就关闭这个连接。
大量 TIME-WAIT 的原因、导致的问题、处理?
- 原因:在高并发短连接的 TCP 服务器上,服务器处理完请求后立刻主动关闭连接,该场景下大量 socket 处于 TIME-WAIT 状态。
- 导致的问题:TIME-WAIT 状态无法真正释放句柄资源,socket 使用的本地端口在默认情况下不能再被使用,会限制有效连接数量,成为性能瓶颈。
- 解决方案:调小 tcp_fin_timeout 的值、将 tcp_tw_reuse 设为 1 开启重用,将 tcp_tw_recycle 设为 1 开启快速回收。
tcp协议的状态及对应的意义?
tcp11种状态及变迁其实基本包含在正常的三次握手和四次挥手中,除开CLOSING。
正常的三次握手包括4中状态变迁:
服务器打开监听(LISTEN)->客户端先发起SYN主动连接标识->服务器回复SYN及ACK确认->客户端再确认即三次握手TCP连接成功。这里边涉及四种状态及变迁:
- LISTEN状态(监听):表示服务器的某个端口正处于监听状态,正在等待客户端连接的到来。
- SYN_SENT状态(同步已发送):当客户端发送SYN请求建立连接之后,客户端处于SYN_SENT状态,等待服务器发送SYN+ACK
- SYN_RECV状态(同步已接收):当服务器收到客户端的连接请求SYN之后,服务器处于SYN_RCVD,在接收到SYN请求之后会向客户端回复一个SYN+ACK的确认报文
- ESTABLISED状态(连接已建立):当客户端回复服务器一个ACK和服务器收到该ACK(TCP最后一次握手)之后,服务器和客户端都处于该状态,表示TCP连接已经成功建立
正常的四次握手包含6种tcp状态变迁,如主动发起关闭方为客户端:
客户端发送FIN进入FIN_WAIT1 -> 服务器发送ACK确认并进入CLOSE_WAIT(被动关闭)状态->客户端收到ACK确认后进入FIN_WAIT2状态 -> 服务器再发送FIN进入LAST_ACK状态 -> 客户端收到服务器的FIN后发送ACK确认进入TIME_WAIT状态 -> 服务器收到ACK确认后进入CLOSED状态断开连接 -> 客户端在等待2MSL的时间如果期间没有收到服务器的相关包,则进入CLOSED状态断开连接。
- FIN_WAIT1状态(终止等待1):当数据传输期间当客户端想断开连接,向服务器发送了一个FIN之后,客户端处于该状态
- FIN_WAIT2状态(终止等待2):当客户端收到服务器发送的连接断开确认ACK之后,客户端处于该状态
- CLOSE_WAIT状态(关闭等待):当服务器发送连接断开确认ACK之后,但是还没有发送自己的FIN之前的这段时间,服务器处于该状态
- TIME_WAIT状态(时间等待):当客户端收到了服务器发送的FIN并且发送了自己的ACK之后,客户端处于该状态
- LAST_ACK状态(最后确认):表示被动关闭的一方(比如服务器)在发送FIN之后,等待对方的ACK报文时,就处于该状态
- CLOSED状态(关闭):结束状态(或初始状态),服务器和客户端都处于该状态,表示TCP连接是“关闭的”或者“未打开的”
CLOSING状态:连接断开期间,一般是客户端发送一个FIN,然后服务器回复一个ACK,然后服务器发送完数据后再回复一个FIN,当客户端和服务器同时接受到FIN时,客户端和服务器处于CLOSING状态,也就是此时双方都正在关闭同一个连接。
在进入CLOSING状态后,只要收到了对方对自己发送的FIN的ACK,收到FIN的ACK确认就进入TIME_WAIT状态,因此,如果RTT(Round Trip Time TCP包的往返延时)处在一个可接受的范围内,发出的FIN会很快被ACK从而进入到TIME_WAIT状态,CLOSING状态持续的时间就特别短,因此很难看到这种状态。
TCP/UDP都是传输层常见的协议,为进程提供通用数据的传输服务。两者的区别?
我们知道网络层,可以实现两个主机之间的通信。但是这并不具体,因为,真正进行通信的实体是在主机中的进程,是一个主机中的一个进程与另外一个主机中的一个进程在交换数据。IP协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信才应该是应用进程之间的通信。
- 是否面向连接:TCP 提供面向连接的服务(通信前先通过三次握手建立连接)。在传送数据之前必须先建立连接,数据传送结束后要释放连接;UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。
ps:关于连接,并不是连在一起,实际上连接是客户端和服务器都维护了一个变量(维护现在数据传输的状态,传了哪些数据,下一次要传哪些数据等等)。UDP通讯有四个参数:源IP、源端口、目的IP和目的端口,tcp在此基础上特有的参数是序列号和应答号,在三次握手的过程中确定相互连接的特性,保证数据传输的安全性。 - 传输是否可靠:TCP 保证数据的可靠传输(三次握手建立连接,有确认、窗口、重传、拥塞控制机制,传输完成断开连接),UDP 使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态。
- 传输形式:TCP 是面向字节流的,因此它能将信息分割成组,并在接收端将其重组;UDP 是面向数据报文段的传输,没有分组的开销。
- 连接对象的个数:TCP 是点到点之间的一对一通信,UDP 支持一对一、一对多和多对多的交互通信。
- 传输效率: TCP 有拥塞控制,UDP 没有拥塞控制,因此网络中出现的拥塞不会降低源主机的发送速率。
- 首部字节:UDP 的首部开销很小,只有 8 字节,相比 TCP 的 20 字节(最小20,最大需要60)要短。
应用场景:UDP协议比TCP协议的效率更高,TCP协议比UDP协议更加安全可靠。
- TCP:当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。建立一个 TCP 连接是需要客户端与服务器端达成三个信息的共识。
1) Socket:由 IP 地址和端口号组成 2) 序列号:用来解决乱序问题等 3) 窗口大小:用来做流量控制
- UDP:效率要求相对较高,对通讯质量要求不严的场景:QQ视频、语音、直播等即时通信场景。
TCP协议如何保证传输可靠?
下面主要对数据传输出现错误/无应答/堵塞/超时/重复等问题。
- 校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
- 确认应答与序列号:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答,也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号(TCP传输时将每个字节的数据都进行了编号,这就是序列号),告诉发送方,接收到了哪些数据,下一次的数据从哪里发。
- 流量控制: TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
- 拥塞控制: 当网络拥塞(对网络中某一资源的需求已经超过该资源所能提供的有效部分),网络性能下降,此时应减少数据的发送。常用的拥塞的手段:慢开始、拥塞避免、快重传与快恢复。
- ARQ协议(停止等待协议): 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
- 超时重传: TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。
- 重复数据丢弃:TCP 的接收端会丢弃重复的数据。
注意:TCP丢包:TCP是基于不可靠的网路实现可靠传输,肯定会存在丢包问题。如果在通信过程中,发现缺少数据或者丢包,那边么最大的可能性是程序发送过程或者接受过程中出现问题。
总结:为了满足TCP协议不丢包,即保证可靠传输,规定如下:
- 数据分片(TCP控制分片大小和重组)
- 达到确认(根据分片数据序号进行确认)
- 超时重传(发从分片时设置定时器,超时未确认,重传)
- 滑动窗口(TCP在滑动窗口的基础上提供流量控制,防止由于传输速率不一致,较快主机致使较慢主机的缓冲区溢出)
- 失序处理(TCP对可能失序的数据报进行重排序,然后交给应用层)
- 重复处理(TCP丢弃重复数据)
- 数据校验(TCP保持它首部和数据的校验和,这个端到端的校验和目的检测传输过程中的变化,如果检验和有差错,丢弃这个分片,也不确认,所以客户端超时重传)
注意:TCP丢包有三方面的原因,一是网络的传输质量不好,二是安全策略,三是服务器性能瓶颈
- 如果网络延迟抖动大,其实不一定会丢包,一般网络拥塞时才会丢包
- 只要网络是通的,TCP有重传机制,肯定会将没传送成功的数据重新发送
什么是滑动窗口机制,为什么会有滑动窗口呢?
先理解2个基础概念:发送窗口、接收窗口
- 滑动窗口是类似于一个窗口(窗口是指一次批量发送多少数据,是缓存的一部分,用来存放字节流),是用来告诉发送端可以发送数据的大小或者说是窗口标记了接收端缓冲区的大小,这样就可以实现流量控制。
工作原理:
- 对于发送端:每收到一个确认帧,发送窗口就向前滑动一个帧的距离;当发送窗口内无可发送的帧时(即窗口内的帧全部是已发送但未收到确认的帧),发送方就会停止发送,直到收到接收方发送的确认帧使窗口移动,窗口内有可以发送的帧,之后才开始继续发送
- 对于接收端:当收到数据帧后,将窗口向前移动一个位置,并发回确认帧,若收到的数据帧落在接收窗口之外,则一律丢弃。
注意点:
- 只有接收窗口向前滑动、接收方发送了确认帧时,发送窗口才有可能(只有发送方收到确认帧才是一定)向前滑动
- 停止-等待协议、后退N帧协议 & 选择重传协议只是在发送窗口大小和接收窗口大小上有所差别
- 当接收窗口的大小为1时,可保证帧有序接收。
- 数据链路层的滑动窗口协议中,窗口的大小在传输过程中是固定的(注意要与TCP的滑动窗口协议区别)
关于滑动窗口的知识点:
- 接收端将自己可以接收的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK来通知发送端;
- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,即就是说不需要接收端的应答,可以一次连续的发送数据;
- 窗口大小字段越大,说明网络的吞吐率越高;
- 操作系统内核为了维护滑动窗口,需要开辟发送缓冲区,来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从缓冲区删掉,注意:发送缓冲区太大,就会有空间开销;
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端,发送端收到这个值后,就会减慢自己的发送速度;
- 如果接收端发现自己的缓冲区满了,就会将窗口的大小设置为0,此时发送端将不再发送数据,但是需要定期个发送一窗口探测数据,段使接收端把窗口大小告诉发送端。注意,在TCP的首部中,有一个16位窗口字段,此字段就是用来存放窗口大小信息的。
滑动窗口中的数据类型:
- 已经发送但是还没确认
- 可以发送,但是还没有发送
谈谈对 ARQ(Auto Repeat reQuest)协议理解(针对出错重传)?
ARQ解决的问题:出现差错时,让发送方重传差错数据:即 出错重传
类型:
- 停止等待协议:每发送完一个分组就停止发送,等待对方确认,在收到确认后再发送下一个分组。
- 连续 ARQ 协议:可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。ps:若信道传输质量很差,导致误码率较大时,后退N帧协议不一定优于停止-等待协议
- 自动重传请求 ARQ 协议:停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求 ARQ。
TCP流量控制和拥塞控制?
流量控制和拥塞控制解决的问题:当接收方来不及接收收到的数据时,可通知发送方降低发送数据的效率:即 速度匹配
流量控制:
- 问题:对于应用程序读取的速度较慢,而发送方发送得太快,就会使接收缓存溢出。所谓的流量控制, 就是告诫对方发送速率不要太快, 要让接收方来得及接收数据。流量控制机制是丢包。
- 解决方案:TCP 通过接收窗口(接收端缓冲区的大小)实现流量控制,接收窗口告诉发送方自己可用的缓存空间,发送方的发送窗口不能超过接收方的接收窗口!注意:TCP窗口的单位是字节, 而不是报文段。
注意:
拥塞控制:
- 问题:网络中的链路容量、交换结点中的缓存与处理机都有着工作的极限,当网络的需求超过他们的工作极限时,就出现了拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
- 解决方案:为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。TCP的拥塞控制采用了四种算法,即慢开始、拥塞避免、快重传和快恢复。
tcp拥塞控制
慢开始与拥塞避免:
- 初始阶段:设置拥塞窗口cwnd = 1,即发送方只能发送一个报文段。
- 慢开始阶段:拥塞窗口cwnd以指数增长
- 当执行慢开始算法时,cwnd初始值为1,发送第1个报文段M0;
- 发送端每收到1个确认,就把cwnd+1,于是发送端可以发送2个报文段M1和M2;
- 接收端发回两个确认,发送端每收到1个确认,就把cwnd+1,于是发送端可以发送4个报文段... ...
- 拥塞避免阶段:拥塞窗口按照线性规律增长
- 当拥塞窗口cwnd增长到慢开始门限slow start thresh(ssthresh),执行拥塞避免算法
- 当拥塞窗口增长到给定值,网络出现超时,即出现网络拥塞,则ssthresh = cwnd / 2
- 更新慢开始门限ssthresh 后,拥塞窗口重新设置为1,重新执行慢开始算法
快重传和快恢复:
- 在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。
- 快重传:在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。
- 快恢复:在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。
- 特别注意:慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。
补充:流量控制和拥塞控制的区别
TCP粘包和拆包
- TCP属于传输层的协议,传输层除了有TCP协议外还有UDP协议。那么UDP是否会发生粘包或拆包的现象呢?答案是不会。UDP是基于报文发送的,从UDP的帧结构可以看出,在UDP首部采用了16bit来指示UDP数据报文的长度,因此在应用层能很好的将不同的数据报文区分开,从而避免粘包和拆包的问题。
- 而TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是(1)TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;(2)另外从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段,基于上面两点,在使用TCP传输数据时,才有粘包或者拆包现象发生的可能。
什么情况造成TCP粘包和拆包?
解决TCP粘包和拆包的方法:
- 发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
- 发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
- 可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。
- 自定义传输协议
UDP怎么设计可靠传输?
传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。
- 添加seq/ack机制,确保数据发送到对端
- 添加发送和接收缓冲区,主要是用户超时重传。
- 添加超时重传机制。
参考
https://www.jianshu.com/p/65605622234b
http://www.open-open.com/lib/view/open1517213611158.html
https://blog.csdn.net/dangzhangjing97/article/details/81008836