以下都是按客户端主动连接方和主动断开连接方

【1】tcp的半关闭状态

  服务器接收到客户端的FIN请求后回复了ACK确认信息,但没有发送FIN请求给客户端,就进入了半连接状态,这时客户端人可以接收服务器传来的数据但不可以发送数据;客户端可以发送数据给客户端单收不到客户端的数据:即客户端单方面断开了连接;客户端和服务器判断连接的是否断开的方法是:通过read();的系统调用返回值为0;linux还有其他连接关闭的一些判断;

【2】tcp连接超时处理

  当客户端对服务器发送请求后长时间得不到回答,客户端必然会发起重连操作(执行多次),若还得不到回应,则通知应用程序连接超时;

【3】tcp复位报文段

  RST报文段;通知对方关闭连接或者重新请求连接,该报文不可被回应;

  a.访问不存在的端口

  b.异常终止连接:以异常方式终止连接;发送复位报文段,终止连接并丢弃发送端所有的数据;

  c.处理半打开连接:当服务器或者客户端异常断开连接后;如果客户端向半打开的连接发送数据服务器则会回复复位报文;

【4】tcp交互数据流

  交互数据仅包含很少的字节,使用交互数据的应用程序对数据的实时性要求很高;

【5】tcp成块数据流

成块数据的长度通常为tcp报文段允许的最大数据长度,对传输效率要求很高,例如:FTP文件传输;

对于tcp的发送缓存区和接收缓存区;tcp报文的标志位p;

【6】tcp滑动窗口

一、滑动窗口

 

  TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现。
  TCP的精髓:滑动窗口协议。
     所谓滑动窗口协议,自己理解有两点:1. “窗口”对应的是一段可以被发送者发送的字节序列,其连续的范围称之为“窗口”;2. “滑动”则是指这段“允许发送的范围”是可以随着发送的过程而变化的,方式就是按顺序“滑动”。在引入一个例子来说这个协议之前,我觉得很有必要先了解以下前提:
  -1. TCP协议的两端分别为发送者A和接收者B,由于是全双工协议,因此A和B应该分别维护着一个独立的发送缓冲区和接收缓冲区,由于对等性(A发B收和B发A收),我们以A发送B接收的情况作为例子;
  -2. 发送窗口是发送缓存中的一部分,是可以被TCP协议发送的那部分,其实应用层需要发送的所有数据都被放进了发送者的发送缓冲区;
  -3. 发送窗口中相关的有四个概念:

    已发送并收到确认的数据(不再发送窗口和发送缓冲区之内)

    已发送但未收到确认的数据(位于发送窗口之中)

    允许发送但尚未发送的数据

    发送窗口外发送缓冲区内暂时不允许发送的数据;
  -4. 每次成功发送数据之后,发送窗口就会在发送缓冲区中按顺序移动,将新的数据包含到窗口中准备发送;
       TCP建立连接的初始,B会告诉A自己的接收窗口大小,比如为‘20’:
       字节31-50为发送窗口

     A发送11个字节后,发送窗口位置不变,B接收到了乱序的数据分组:

     只有当A成功发送了数据,即发送的数据得到了B的确认之后,才会移动滑动窗口离开已发送的数据;同时B则确认连续的数据分组,对于乱序的分组则先接收下来,避免网络重复传递:


二、流量控制
     流量控制方面主要有两个要点需要掌握。一是TCP利用滑动窗口实现流量控制的机制;二是如何考虑流量控制中的传输效率。
1. 流量控制
  所谓流量控制,主要是接收方传递信息给发送方,使其不要发送数据太快,是一种端到端的控制。主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送:
  这里面涉及到一种情况,如果B已经告诉A自己的缓冲区已满,于是A停止发送数据;等待一段时间后,B的缓冲区出现了富余,于是给A发送报文告诉A我的rwnd大小为400,但是这个报文不幸丢失了,于是就出现A等待B的通知||B等待A发送数据的死锁状态。为了处理这种问题,TCP引入了持续计时器(Persistence timer),当A收到对方的零窗口通知时,就启用该计时器,时间到则发送一个1字节的探测报文,对方会在此时回应自身的接收窗口大小,如果结果仍未0,则重设持续计时器,继续等待。
2. 传递效率
     一个显而易见的问题是:单个发送字节单个确认,和窗口有一个空余即通知发送方发送一个字节,无疑增加了网络中的许多不必要的报文(请想想为了一个字节数据而添加的40字节头部吧!),所以我们的原则是尽可能一次多发送几个字节,或者窗口空余较多的时候通知发送方一次发送多个字节。对于前者我们广泛使用Nagle算法,即:
  *1. 若发送应用进程要把发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面的字节先缓存起来;
  *2. 当发送方收到第一个字节的确认后(也得到了网络情况和对方的接收窗口大小),再把缓冲区的剩余字节组成合适大小的报文发送出去;
  *3. 当到达的数据已达到发送窗口大小的一半或以达到报文段的最大长度时,就立即发送一个报文段;
 对于后者我们往往的做法是让接收方等待一段时间,或者接收方获得足够的空间容纳一个报文段或者等到接受缓存有一半空闲的时候,再通知发送方发送数据。

Recv函数

int recv(SOCKET s,charFAR*buf,int len,int flags)

s:指定接收端套接字描述符

Buf:指明一个缓存区用来存放recv函数接收到的数据

Len:指明buf的长度

第四个参数一般置为0

 

【6】tcp外带数据

紧急指针标志和紧急指针;

 

紧急指针的位置—tcp的报文序号的到紧急数据的位置;

【7】tcp超时重传

  tcp维护一个重传定时器;如果超时未得到回答将会重新定制重传计时器;默认重传次数最少是三次,最多十五次一半对应13-30 min;

【8】tcp拥塞控制

  a.拥塞控制概述:

  调高网络利用率,降低丢包率,保证网络资源对每条数据的公平性;

  如果网络上的延迟突然增加,那么,tcp对这个事做出的应对只有重传数据,然而重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这就导致了恶性循环,最终形成了网络风暴——tcp拥塞控制机制就是用于应对这种情况(控制发送窗口SWDN的大小以及发送SWDN的过程)

  拥塞控制的最终受控变量是发送端向网络一次连续写入的数据量,我们称为SWND(发送窗口);不过,发送端最终以tcp报文段来发送数据,所以SWND限定了发送端能连续发送的tcp报文段数量(SMSS,一般等于MSS);发送端需要合理的选择SWDN的大小,如果SWDN太大对导致网络延迟;SWDN太小会导致网络拥塞;接收方可以通过接收通告窗口(RWDN)来控制SWDN;但这显然不够,所有以发送端引入了一个称为拥塞窗口的状态量(CWDN);实际的SWDN是min(CWDN,SWDN);如图显示了拥塞控制的输入和输出(可见,它是一个闭环反馈控制)

 

 

 

  b.慢启动和拥塞避免:

  TCP建立好连接后,CWDN被初始化为IW(一般为2~4 SMSS);此后发送端每收到一个ACK,CWDN += min(N,SMSS);其中N是此次确认(包含之前未被确认的)的字节;这样一来CWDN将按照指数去生长;

  阈值ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入拥塞避免算法

  拥塞避免算法:当拥塞窗口 cwnd 达到一个阈值时,窗口大小不再呈指数上升,而是线性上升,避免增长过快导致网络拥塞。 每当收到一个ACK,CWDN += SMSS*SMSS/CWDN,每当过了一个RTT,CWDN += min(N,SMSS)

  c.快速重传和快速恢复:

  以上是发送端未检测到拥塞是采用得拥塞避免算法,加下来介绍拥塞发生时的拥塞控制行为;

  发送端判断拥塞产生的依据:

  1)传输超时,或者TCP重传定时器溢出;

  2)接收到重复的ACK确认报文段;

拥塞控制对这两种情况有两种不同的处理方法,对于第一种情况仍然使用慢启动和拥塞避免的算法;

对于第二种情况则使用快速重传和快速恢复算法(如果真的发生拥塞时);若第二种情况发生在定时器溢出后按第一种情况处理;

当发送端检测到拥塞发生是第一种情况后启动超时重传,并且做出如下处理: