状态

①带有状态的三次握手

传输层TCP二

在该例中,A为主动打开链接,B为被动打开连接

①A的TCP客户进程首先创建传输控制模块TCB。然后打算建立TCP连接时,向B发送请求报文段,这时首部的同步位SYN=1,同时选择一个初始序号seq=x。TCP规定,SYN报文段(即SYN =1的报文段)不能携带数据,但要消耗一个序号。这时,TCp客户进程进入SYN-SENT(同步已发送)状态。
②B收到请求报文段之后,如果同意连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时也为自己选择一个初始序号seq = y。注意,这个报文段也不能携带数据,但同样也要被消耗一个序号。这时,TCP服务器进程进入SYN-RECVD(同步收到)状态。
③ TCP客户端收到B的确认后,还要向B发送确认。确认报文段的ACK置1,确认序号ack = y +1 ,而自己的序号seq = x + 1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是 seq = x + 1。 这时,TCP连接已建立,A进入ESTABLISHED(已建立连接)状态。
④当B收到A的确认后,也进入ESTABLISHED状态。

上面给出的建立连接过程也叫三次握手。注意:在B给A发送的报文段,也可以拆成两个报文段。可以先发送一个确认报文段(ACK =1 ,ack = x + 1),然后再发送一个同步报文段(SYN = 1 , seq = y)。这样的过程变成了四报文握手,但效果是一样的。

为什么A最后还要发送一次请求呢?这主要是为了防止已失效的连接请求报文段突然传送到了B,因而产生错误

注意:传输控制块TCB(Transmission Control Block)存储了每一个连接中的一些重要信息,如:TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号,等等

②TCP的连接释放

①数据传输结束后,通信的双方都可释放连接,现在A和B都处于ESTABLISHED状态。A的应用进程向其TCP发送连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FIN置1,其序号seq = u,它等于前面已传送的数据的最后一个字节的序号加1,这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。注意,TCp规定,FIN报文段即使不携带数据,它也消耗一个序号。
传输层TCP二

②B收到连接释放报文段之后即发送确认,确认号是ack = u +1 ,而这个报文段自己的序号是v,等于B 前面已传送的数据的最后一个字节的序号加1。然后B进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时就通知高层应用进程,因而这从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一段时间(因为B到A方向的连接此刻关不关闭看B自己)。
③A收到来自B的确认后,就进入FIN-WAIT-2(终止等待状态2),等待B发出连接释放报文段。
③若B已经没有向A发送的数据,其应用进程就通知TCP释放连接。这时,B发送的连接释放报文段必须使FIN=1。现在假定B的序号位W(在半关闭的状态B可能又给A发送了一些数据),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才能进入到CLOSED状态。时间MSL叫做最长报文段寿命(Maximum Segment Lifetime),RFC 793 建议设为2分钟。

以上TCP连接释放过程是四次报文握手

除了时间等待计时器,TCP还设有一个保活计时器(keepalive timer)。这个就是用来防止客户端出现故障,或者长时间对服务器没有活动,而浪费服务器资源。通常设置的时间位2小时,超过这个时间,没隔75s发送一个探测报文,若一连发送10个探测报文后,客户端没有响应,服务器对客户端按出故障处理,关闭连接。

TCP的有限机状态

传输层TCP二

ClOSE-WAIT

一般而言,对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有
正确完成. 这是一个 BUG. 只需要加上对应的socket.close()即可解决问题。

再来看看标志位

URG 紧急指针是否有效
ACK:确认号是否有效
ack:从上的握手中,该标志位用来告诉对方,期待下一次收到序号从那开始
PSH:提示接收端应用层立刻从TCP缓存区读走
RST:对方要求重新建立连接,我们把携带RST标识的称为复位报文段
SYN:请求建立连接,把携带SYN标识的称为同步报文段
FIN:通知对方,本端要关闭了,结束报文段

TCP的可靠性

确认应答+超时重传+连接管理

为什么说TCP是面向字节流的

因为TCP考虑了对方的接收能力以及传输信道是否拥塞的情况。比如这样一种情况:想UDP一样,应用进程给1M的数据,而对方接收能力是现在能接收500k的数据,是像UDP一样,直接发送出去还是怎么办。直接发送出去,就会导致对方接收500k数据,剩余500k丢弃,接收方UDP会交给应用进程,但是是个错误的包,按照TCP的性格,你发送了1M,我接收了500K,再和数据的首部一对比,这个是错误的,整个都会丢弃,接收方的应用层根本接收不到。所以就有了流量控制和拥塞机制。把要发送的数据进行拆分发送,因为数据的首部序号,检验和信息,能保证接收方利用缓存区最终接收的数据是发送方要发送的数据(在整个传输过程没有出错的情况下),所以面向字节流是因为以上原因导致的结果。

①TCP的流量控制

所谓流量就是让发送方发送发送的速率不要太快,要让接收方来得及接收。利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制

滑动窗口

现在回想TCP报文段首部中窗口的作用,这个窗口有两种,一种是,一种是接收方的接收窗口,这个值由接收方的缓存区大小决定,由接收方每次应答发送方时告诉,当接收方的缓存区满了,就会将窗口值置0,这时发送方就不再发送数据了,但需要定时发送一个探测报文段,让接收方把窗口值告诉发送方,好进行下一次的发送。另一种时拥塞窗口,这个值是由网络资源决定,而最终由接收方动态推测。窗口值的大小取,两个窗口值的最小值。

窗口定义了一个边界,分别为左边界,右边界,边界的值对应的是字节的序号,比如起始序号seq = 1,数据长度是300,窗口值是20,则左边界值为1,右边界为20,滑动窗口是移动两个边界,一般来说,左边界不能向左边滑动,顺着时间增大的方向滑动,右边界由下一次的窗口值决定,左边界的值由下一次期望数据从那个序号开始。因此,发送方的窗口不能超过接收方给的接收窗口的数值。

TCP的拥塞机制

在计算机网络中的链路容量(即宽带),交换结点中的缓存和处理机制等,都是网络的资源,在某段时间内,若对网络中的某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏,这种情况就是拥塞。

所谓拥塞机制就是为了防止过多的数据注入到网路中,这样可以使网络中的路由器或链路不致过载,提高传输效率,而流量控制则是往往是指点对点通信量的控制。刚好能帮助拥塞控制,提高传输效率。

判断网络拥塞的依据就是出现了延时
——————————————————————-
拥塞窗口的大小变化

慢开始:当主机发送数据时,由于不清楚网络的负荷情况,如果立即把大量的数据注入到网络中,可能会引起网络发生拥塞,较好的办法是先探测一下,即由小到大逐渐增大发送窗口数值

缓存区写满了,应用层怎么给缓存区写数据

作为应用层,有责任要考虑数据没有被写入的情况,JAVA的os.write()底层应用了一个循环实现,如果缓存区满了,会一直等,直到写满才返回

相关文章: