TCP是TCP/IP体系中非常复杂的一个协议,它有以下特点:
TCP是面向连接的运输层协议
。这就是说,应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。这就是说,应用进程之间的通信好像在“打电话”:通话前要先拨号建立连接,通话结束后要挂机释放连接。每一条TCP连接只能有两个端点, 每一 条TCP连接只能是点对点的(一对一)
TCP提供可靠交付的服务
。也就是说,通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。TCP提供全双工通信
。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,.上层的应用进程在合适的时候读取缓存中的数据。 面向字节流
。TCP中的“流"指的是流入到进程或从进程流出的字节序列。面向字节流” 的含义是:虽然应用程序和 TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。也就是说TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。总的来说:TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议
在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做ACK(确认应答)。
通常,两个人对话时,在谈话的停顿处可以点头或询问以确认谈话内容。如果对方迟迟没有回应,说话的一方还可以重复一遍以保证对方确实可以听到。因此,对方是否理解了此次对话的内容以及是否听到了对话的内容都要靠对方的反应来判断。
网络中的确认应答就类似于上面的例子。当对方听懂对话内容时会说:“嗯”,这就相当于返回了一个确认应答(ACK)。而当对方没有理解对话内容或没有听清时往往会问一句“啊?”这好比一个否定确认应答
TCP通过肯定的ACK实现可靠的数据传输。当发送端将数据发出之后会等待对端的确认应答,如果有确认应答,说明数据已经成功到达,如果没有,那么数据有可能丢失了。
如下图所示,在一定时间内没有等到确认应答,发送端就可以认为数据已经丢失,就会进行重发。由此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。
除此之外,未收到确认应答并不意味着数据一定丢失。也有可能是数据对方已经收到,只是返回的确认应答在途中丢失。这种情况也会导致发送端因没有收到确认应答,而认为数据没有到达目的地,从而进行重新发送。如下图所示:
此外,还有一种情况对于接收方来说会比较难受。就是确认应答延时到达,在等待的过程中发送端会误认为数据丢失,所以会反复触发重传,因此对于接受主机来说,它会反复接收到相同的数据,而为了对上层应用提供可靠的传输,就必须得放弃重复的数据包。为此就必须要引入一种机制,使其能够识别是否已经接受数据,又能判断是否需要接受。
序列号是按照顺序给发送数据的每一个字节都标上号码的编号。接收端查询接收数据TCP首部中的序列号和数据的长度,将自己下一步应该接受的序号作为确认应答返送回去。就这样,通过序列号和确认应答号,TCP可以实现可靠传输
时重传的超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔。如果超过了这个时间仍然为收到确认应答,发送端将会重发数据。那么这个数据是如何确定的呢?
最理想就是找到一个最小时间,它能保证确认应答一定能在这个时间内返回。但是这个时间长短随着数据包途径的网络环境的不同会有所变化。
TCP要求不论处在何种网络环境下都要提供高性能通信,而且无论网络拥堵的情况发生如何改变,都必须保持这一特性。为此,它在每次发包时都会计算往返时间及其偏差,将这个往返时间和偏差相加重发超时的时间,就是比这个综合要稍微大一点的值。
在BSD的Unix及Windows系统中,超时都以0.5s为单位进行控制,因此重发超时都是0.5s的整数倍。不过由于最初的数据包还不知道往返时间,所以其重发超时一般设置为6s左右
数据被重发以后若还是收不到应答,就会进行再次发送,此时,等待确认应答的时间将会以2倍,4倍的指数函数延长
不过,数据也不会无限,反复的重发。当达到一定的重发次数之后,如果仍然没有任何确认应答返回,就会判断为网络或对端主机发生异常,强制关闭连接。
TCP提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好通信两端之间的准备工作。这里的准备工作就是建立连接,也就是我们常说的三次握手。不需要通信时,就需要释放连接,以免浪费资源,这也是我们常听说的四次挥手。在正常情况下,TCP要经过三次握手建立连接, 四次挥手断开连接
三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。
最初两端的TCP进程都处于CLOSED(关闭)状态。请注意,A主动打开连接,而B被动打开连接。
B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求。然后服务器进程就处于LISTEN (收听)状态,等待客户的连接请求。如有,即作出响应。
第一次握手
:A的TCP客户进程也是首先创建传输控制模块TCB,然后向B发出连接请求报文段,这时首部中的同步位SYN = 1, 同时选择-一个初始序号seq = x。TCP规定,SYN报文段(即SYN = 1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN- SENT(同步已发送)状态。第二次握手
:B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack=x + 1,同时也为自己选择一个初始序号seq= y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。 这时TCP服务器进程进入SYN-RCVD (同步收到)状态。第三次握手
:TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+ 1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq=x+ 1。这时,TCP连接已经建立,A进入ESTABLISHED (已建立连接)状态。当B收到A的确认后,也进入ESTABLISHED状态。在socket编程中,客户端执行connect()时,将触发三次握手
为什么是三次握手?一次,两次,四次不行吗?
四次不行。我们知道,在通信时收到的报文总是对上一次发送报文的确认,无论四次、五次,或者多次,总有最后一次是无法被确认的。也就是说,最后一次都有可能是失败的,并且还可能让服务器建立大量的无用连接,从而浪费资源。为了让服务器
不要出现误判行为,减小因握手带来的时间消耗,三次是最佳选择。
一次不行。如果客户端发送一次请求报文就进入已连接状态,而服务端收到这个报文也进入连接状态,连接就一定成功吗?不一定,请求报文可能在中途丢失,这既不能验证客户端的收发能力,也不能验证服务端的收发能力。
二次也不行。只能验证客户端收发能力,服务端的接受能力,不能验证服务端的发送能力。现假定出现这样一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。采用三次握手的办法可以防止上述现象的发生。例如在刚才的情况下,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。
一次或两次不行,还有一个原因就是服务端受到攻击的成本太低。例如,一些恶意用户进行大量的SYN请求,导致服务端必须维持大量的连接,从而耗尽资源。这被称为SYN洪水攻击。
TCP的三次握手是否都可以携带数据?
TCP三次握手失败,服务端会如何处理?
握手失败的原因有两种:
数据传输结束后,通信的双方都可释放连接。最开始A和B都处于ESTABLISHED状态
第一次挥手
:A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的FIN置1,其序号seq=u,它等于前面已传送过的数据的最后一个字节的序号加1。这时A进入FIN-WAIT-1 (终止等待1)状态,等待B的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。第二次挥手
:B收到连接释放报文段后即发出确认,确认号是ack=u+ 1,而这个报文段自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1。然后B就进入CLOSE-WAIT (关闭等待)状态。TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭。这个状态可能会持续一些时间。A收到来自B的确认后,就进入FIN-WAIT-2 (终止等待2)状态,等待B发出的连接释放报文段。第三次挥手
:若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN = 1。现假定B的序号为w (在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack= u + 1。这时B就进入LAST-ACK (最后确认)状态,等待A的确认。第四次挥手
:A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack=w + 1,而自己的序号是seq=u+ 1 (根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT (时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器设置的时间2MSL后,A才进入到CL OSED状态,才能开始建立下一个新的连接。当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。以上就是四次挥手的全部过程,下面将逐一解答一些问题。
为什么A在TIME-WAIT状态必须等待2MSL的时间呢?
为什么握手是三次,而挥手时需要四次呢?
CLOSE_WAIT状态详谈
:
TIME_WAIT状态详谈
:
确保最后一个确认报文能够到达
。进而尽快释放服务器的资源。如果没能到达,服务端就会重发FIN请求释放连接。等待一段时间没有收到重发就说明服务的已经CLOSE了。如果有重发,则客户端再发送一次ACK信号如何解决TIME_WAIT状态引起的bind失败?
在server的TCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的
使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符
TCP以1个段为单位,每发-一个段进行-一次确认应答的处理。这样的传输方式有一个缺点。那就是,包的往返时间越长通信性能就越低。
为解决这个问题,TCP引入了窗口这个概念。即使在往返时间较长的情况下,它也能控制网络性能的下降。确认应答不再是以每个分段,而是以更大的单位进行确认时,转发时间将会被大幅度的缩短。也就是说,发送端主机,在发送了一个段以后不必要一直等待确认应答,而是继续发送。
那么如果出现了丢包, 如何进行重传? 这里分两种情况讨论
情况一: 数据包已经抵达, ACK丢了
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认。
这种机制也叫快速重传
。一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收,或者发送方的速度太慢,接收方希望发送方发送的速度变快一些
利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。
具体操作:
接收端何时
把窗口大小告诉发送端呢?
三次握手的时候告诉对方
TCP首部中,有一个16位窗口字段,就是存放了窗口大小信息。
那么问题来了,16位数字最大表示65535,那么TCP窗口最大就是65535字节么?
不是,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是 窗口字段的值左移M位。
有了TCP的窗口控制,收发主机之间即使不再以一个数据段为单位发送确认应答,也能够连续发送大量数据包。然而,如果在通信刚开始时就发送大量数据,也可能会引发其他问题。
一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果突然发送一个较大量的数据,极有可能会导致整个网络的瘫瘓。
TCP为了防止该问题的出现,在通信一开始时就会通过一个叫做慢启动的算法得出的数值,对发送数据量进行控制。
当TCP通信开始以后,网络吞吐量会逐渐上升,但是随着网络拥堵的发生吞吐量也会急速下降。于是会再次进人吞吐量慢慢上升的过程。
接收数据的主机如果每次都立刻回复确认应答的话,可能会返回一个较小的窗口。那是因为刚接收完数据,缓冲区已满。当某个接收端收到这个小窗口的通知以后,会以它为上限发送数据,从而又降低了网络的利用率。为此,引人了一个方法,那就是收到数据以后并不立即返回确认应答,而是延迟一段时间的机制。
那么所有的包都可以延迟应答么?
肯定也不是
数量限制: 每隔N个包就应答一次
时间限制: 超过最大延迟时间就应答一次
具体的数量和超时时间,依操作系统不同也有差异;。一般N取2,超时时间取200ms。
事实上,大可不必为每一个数据段都进行一次确认应答。TCP采用滑动窗口的控制机制,因此通常确认应答少一些也无妨。TCP文件传输中,绝大多数是每两个数据段返回一次确认应答。
TCP的确认应答和回执数据可以通过一个包发送。这种方式叫做捎带应答。通过这种机制,可以使收发的数据量减少。
另外,接收数据以后如果立刻返回确认应答,就无法实现捎带应答。而是将所接收的数据传给应用处理生成返回数据以后进再进行发送请求为止,必须一直等待确认应答的发送。也就是说,如果没有启用延迟确认应答就无法实现捎带应答。延迟确认应答是能够提高网络利用率从而降低计算机处理负荷的一种较优的处理机制。
TCP要保证可靠性,同时又尽可能的提高性能
可靠性:
提高性能
: