TCP笔记

转载自:http://blog.csdn.net/kobejayandy/article/details/38933499
    http://blog.csdn.net/kobejayandy/article/details/38933513
    http://blog.csdn.net/kobejayandy/article/details/18183077
    http://blog.csdn.net/kobejayandy/article/details/46641791
    http://blog.csdn.net/xifeijian/article/details/44260995

TCP报文格式

TCP协议的报文格式如下:

标志位

  • URG:指示报文中有紧急数据,应尽快传送(相当于高优先级的数据)。
  • PSH:为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
  • RST:TCP连接中出现严重差错(如主机崩溃),必须释放连接,在重新建立连接。
  • FIN:发送端已完成数据传输,请求释放连接。
  • SYN:处于TCP连接建立过程。 (Synchronize Sequence Numbers)
  • ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。

窗口
滑动窗口大小,这个字段是接收端用来告知发送端自己还有多少缓冲区可以接受数据。于是发送端可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。(以此控制发送端发送数据的速率,从而达到流量控制。)窗口大小时一个16bit字段,因而窗口大小最大为65535。

头部长度(首部长度)
由于TCP首部包含一个长度可变的选项和填充部分,所以需要这么一个值来指定这个TCP报文段到底有多长。或者可以这么理解:就是表示TCP报文段中数据部分在整个TCP报文段中的位置。该字段的单位是32位字,即:4个字节。TCP的滑动窗口大小实际上就是socket的接收缓冲区大小的字节数。

选项和填充部分
TCP报文的字段实现了TCP的功能,标识进程、对字节流拆分组装、差错控制、流量控制、建立和释放连接等。其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,那么选项部分最长为:(2^4-1)*(32/8)-20=40字节。

TCP三次握手

  所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。

  最开始的时候客户端和服务器都是处于 CLOSED 状态。主动打开连接的为客户端,被动打开连接的是服务器。
  第一次握手: 客户端向服务器发出连接请求报文,这时报文的 标志位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 **SYN-SENT(同步已发送状态)状态。
  第二次握手: 服务器发回确认包(ACK)应答。确认包中 ACK=1,SYN=1,确认序号(Acknowledgement Number)是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,服务器进入了
SYN-RCVD(同步收到)状态。
  第三次握手: 客户端收到确认后,再次向服务器发送确认包(ACK)。SYN标志位为0,ACK标志位为1,ack=y+1,seq=x+1。此时,TCP连接建立,客户端进入
ESTABLISHED(已建立连接)**状态。当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。

  三次握手主要目的是:防止超时导致脏连接。
  如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

SYN攻击

  在三次握手过程中,服务器发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect).此时服务器处于SYN_RECV状态.当收到ACK后,服务器转入ESTABLISHED状态。
  如果server端接到了client发的SYN后返回了SYN-ACK,之后client掉线了,server端没有收到client回来的ACK,那么,这个连接处于一个中间状态,即没成功,也没失败。于是,server端如果在一定时间内没有收到的ACK会重发SYN-ACK。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才会把断开这个连接。
  SYN Flood攻击就是攻击客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送SYN包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
  SYN攻击是一个典型的DDOS攻击。检测SYN攻击非常的方便,当服务器上出现大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击.在Linux下可以如下命令检测是否被SYN攻击: netstat -n -p TCP | grep SYN_RECV。
  在linux中使用SYN cookie或SYN COOKIE 防火墙来应对SYN Flood攻击。

TCP 四次挥手

  数据传输完毕后,双方都可释放连接。TCP的连接的断开需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作。
  TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

  TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入**FIN-WAIT-1(终止等待1)状态。
  服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号+1,并且带上自己的序列号seq=v,此时,服务端就进入了
CLOSE-WAIT(关闭等待)状态。这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
  客户端收到服务器的确认请求后,此时,客户端就进入
FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
  服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,此时,服务器就进入了
LAST-ACK(最后确认)状态,等待客户端的确认。
  客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,此时,客户端就进入了
TIME-WAIT(时间等待)**状态。注意此时TCP连接还没有释放,必须经过2MSL(报文最大生存时间)的时间后,才进入CLOSED状态。
  服务器只要收到了客户端发出的确认,立即进入CLOSED状态。

为什么客户端最后还要等待2MSL?

  MSLMaximum Segment Lifetime)报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。2MSL即两倍的MSL。
  当TCP的一端发起主动关闭,在发出最后一个ACK包后,就进入了TIME_WAIT状态,必须在此状态上停留2MSL时间,等待2MSL时间主要目的是 保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失。假设最后一个ACK包丢了,那么对方在超时后将重发FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包,并且会重启2MSL计时器。在TIME_WAIT状态时两端的该端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。如果客户端收到服务端的FIN+ACK报文后,发送一个ACK给服务端之后就立马进入CLOSED状态,可能会导致服务端无法确认收到最后的ACK指令,也就无法进入CLOSED状态。

  在TIME_WAIT状态无法真正释放句柄资源,在此期间,Socket中使用的本地端口在默认情况下不能再被使用。该限制对于客户端机器来说是无所谓的,但对于高并发服务器来说,会极大地限制有效连接的创建数量,称为性能瓶颈。所以建议将高并发服务器TIME_WAIT超时时间调小。RFC793中规定MSL为2分钟。但是在当前的高速网络中,2分钟的等待时间会造成资源的极大浪费,在高并发服务器上通常会使用更小的值。
  在服务器上通过变更/etc/sysctl.conf文件来修改该默认值net.ipv4.tcp_fin_timout=30(建议小30s)。修改完之后执行 /sbin/sysctl -p让参数生效。
  通过如下命令查看各连接状态的计数情况:

# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 63
ESTABLISHED 13

  也可以通过设置SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。

滑动窗口

  TCP通过滑动窗口的概念来进行流量控制。设想在发送端发送数据的速度很快而接收端接收速度却很慢的情况下,为了保证数据不丢失,显然需要进行流量控制,协调好通信双方的工作节奏。
  所谓滑动窗口,可以理解成接收端所能提供的缓冲区大小。TCP利用一个滑动的窗口来告诉发送端对它所发送的数据能提供多大的缓冲区。由于TCP数据报中窗口标志由16位bit所定义,所以接收端TCP能最大提供65535个字节的缓冲。由此,可以利用窗口大小和第一个数据的序列号计算出最大可接收的数据序列号。
  滑动窗口本质上是描述接受方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据。如果发送方收到接受方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接受方发送窗口大小不为0的数据报的到来。
  窗口合拢(左移):在收到对端数据后,自己确认了数据的正确性,这些数据会被存储到缓冲区,等待应用程序获取。但这时候因为已经确认了数据的正确性,需要向对方发送确认响应ACK,又因为这些数据还没有被应用进程取走,这时候便需要进行窗口合拢,缓冲区的窗口左边缘向右滑动。这种现象发生在数据被发送和确认的时候。
  窗口张开(右移):窗口收缩后,应用进程一旦从缓冲区中取出数据,TCP的滑动窗口需要进行扩张,这时候窗口的右边缘向右扩张,实际上窗口这是一个环形缓冲区,窗口的右边缘扩张会使用原来被应用进程取走内容的缓冲区。在窗口进行扩张后,需要使用ACK通知对端,这时候ACK的序号依然是上次确认收到包的序号。这种现象发生在接受端处理了数据以后。
  窗口收缩:窗口的右边缘向左滑动,称为窗口收缩,这种现象不常发生。
  TCP就是用这个窗口,慢慢的从数据的左边移动到右边,把处于窗口范围内的数据发送出去(但不用发送所有,只是处于窗口内的数据可以发送。)。这就是窗口的意义。窗口的大小是可以通过socket来制定的,4096并不是最理想的窗口大小,而16384则可以使吞吐量大大的增加。

慢启动(拥塞窗口)

  数据在传输的时候不能只使用一个窗口协议,还需要有一个拥塞窗口来控制数据的流量,使得数据不会一下子都跑到网路中引起“拥塞”。拥塞窗口最初使用指数增长的速度来增加自身的窗口,直到发生超时重传,再进行一次微调。
  拥塞窗口(cwnd)原理:
  1.发送方开始发送一个报文,然后等待ACK,当收到该ACK时,拥塞窗口从1增加到2,即可以发送2个报文段,然后等待ACK,当收到这两个报文段的ACK时,拥塞窗口就增加为4。这是一种指数增加的关系。该过程即慢启动。
  2.当拥塞窗口达到慢启动门限后,拥塞窗口不再以指数增长,而是线性增长,直到拥塞发生(超时或收到重复确认)。该过程即拥塞避免阶段。
  3.当发生拥塞时,慢启动门限会重新设置为当前窗口的一半,拥塞窗口被设置为1,当新的数据被确认后,在指数增加拥塞窗口,重复上述过程。
  在TCP Reno版本中增加了快速重传和快速恢复算法。
  快速重传:当发送方收到三个重复的ack后,不会进入慢启动状态,而是立刻重传丢失的报文。因为只有接收方收到新的报文段的时候,才会发送重复的ack,这表明TCP连接上仍然有数据流动,所以应该避免使用慢启动降速。
  快速恢复算法:快速重传之后,接下来执行的不是慢启动而是拥塞避免算法。
  慢启动算法用于保证新分组进入网络的速率与另一端返回确定的速率相等。拥塞窗口是发送使用的流量控制,通告窗口是接收方使用的流量控制。
  总的来说,慢启动、拥塞避免、快速重传和快速恢复是TCP拥塞控制的算法。

定时器

延时定时器
  TCP中对数据的确认往往是延迟的,一般情况是两个TCP数据对应一个确认,在时延定时器没有溢出的情况下。如果时延定时器溢出了,那么自然也会发送确认报文。当TCP收到报文时候,启动延时定时器,比如200ms。

重传定时器
  目的是为了获得对端的确认报文。如果多次重传仍然没有获得确认,则会发送复位报文RST。

坚持定时器
  由于TCP没有对ACK的确认机制,所以当接收端窗口从0恢复到一定值的时候,如果接收端发给发送端的ACK报文(标识窗口大小)丢失了,发送端就永远不知道接收端的窗口恢复情况了。所以发送端会定时发送带一个字节的ACK给接收端,查看接收端的确认报文中的窗口信息。

保活定时器
  由于物理原因,处于空闲状态的TCP连接一端崩溃的时候,TCP有保活机制来判断对端是否仍然工作。保活定时器默认是关闭的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值