tcp/ip ---------- tcp(1)

本文深入解析了TCP连接的建立、维护、关闭过程,包括三次握手与四次挥手的原理,TCP首部结构与功能,MSS、选项与状态转换图等内容,详细阐述了TCP如何确保数据可靠传输。

面向连接意味着两个使用TCP的应用在彼此交换数据之前必须先建立一个TCP连接

TCP可靠性:

1、  数据被分割成TCP认为最适合发送的数据块

2、  当TCP发出一个报文段后,它启动一个定时器,等待对端确认。超时重传

3、  TCP收到另一端来的数据,将发送一个确认,不是立即发送,通常会推迟

4、  检测检验和,出错则丢弃且不发送确认,等待重传

5、  对数据进行重新排序, 丢弃重复数据

6、  流量控制,防止缓冲区溢出

 

TCP首部:

                           

TCP被封装在IP数据报中:

                                      

对于TCP首部:

         序号用来标识从TCP发端向TCP收端发送数据字节流,表示在这个报文段中的第一个数据字节(SYN和FIN都要消耗一个序号)

         确认序号是上次已成功接收到数据字节序号加1。只有ACK标识为1时确认序号才有效。发送ACK无需代价,一旦一个链接建立起来,这个字段总被设置,ACK标识总被置1.

         4位首部长决定了TCP首部最多60字节,若没有任何选项,正常长度为20

         URG: 紧急指针, ACK : 确认序号, PSH : 接收方应尽快将这个报文段交给应用层

         RST: 重建连接, SYN , FIN

 

TCP检验和,包含首部和数据,是强制的,也使用和UDP类似的伪首部

只有设置了URG标识紧急指针才有效

 

 

为什么建立连接要三次握手,而终止连接却需要四次握手?

         这是由TCP的半关闭造成的。既然一个TCP连接时全双工的,因此每个方向必须单独进行关闭。即当一方完成了数据传输任务后就可以发送一个FIN来终止这个连接。是使用shutdown函数。

 

 

TCP选项,最大报文段长度,MSS

         当一个连接建立时,连接的双方都要通告各自的MSS。

         如果一方不接受来自另一方的MSS值,则MSS默认为536(这样就允许20字节IP首部和20字节TCP首部适合576字节的IP数据报),对于一个以太网,MSS值可达1460字节

         MSS让主机限制另一端发送数据报的长度,加上主机能控制它发送数据报的长度,这将使以较小MTU连接到一个网络上的主机避免分段

         但是,如果两段的主机都连接到以太网上,都采用536的MSS,但中间网络采用296的MTU,也将会出现分片,使用路径上的MTU发现机制可以处理这个问题。

 

 

TCP状态转换图

                                               

 

关于这里的每个状态:

                                                                  

CLOSED:

这个没什么好说的了,表示初始状态。

LISTEN:

这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。


SYN_RCVD:


 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。


SYN_SENT:

这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。


ESTABLISHED

这个容易理解了,表示连接已经建立了。


FIN_WAIT_1:

这个状态要好好解释一下,其实FIN_WAIT_1FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKETESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。


FIN_WAIT_2

FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。


CLOSING:

这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。


CLOSE_WAIT:

这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。


LAST_ACK:

这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

 

TIME_WAIT

提出主动关闭连接的一端往往会进入TIME_WAIT状态。

TIME_WAIT状态存在的意义是什么呢?

      我们知道,四分组终止连接序列中,发起连接中断的一端也是发送最后一个ACK的那段,正因为这样,我们有以下两个解释:

        1、本端发出的最后一个ACK,对端不一定接收到。如果这个ACK丢失了,那么对端会重复发出最后一个FIN,因此本端要维持一个状态以保证将来能重复发送最后一个ACK,如果我们不维持这个状态,而是处于CLOSED状态,那么本端将回复RST而不是ACK,另一端则会将此解释为一个错误。

         2、允许之前在网络中迷失的分组消逝在网络中。可能会在双方连接的过程中有分组走失了,那么最终它找到目的地址后,发现我们依旧是这个状态在等待它,然后正确的处理它。可是如果我们不是这个状态,而是立刻又开启了一个连接,那么老分组就会被新的连接接收到了,这会引发系列错误。

 

 

对于TIME_WAIT状态,我们知道往往是执行主动关闭的一方会进入这个状态。如果我们终止一个客户程序,并立即重启它,那么它将不会重用相同的本地端口,只是这并没关系,因为我们不关心客户使用的哪个端口。

但是对于服务器来说,服务器使用的是熟知端口,重启往往会被禁止,因为端口正处于TIME_WAIT状态。这时候我们可以使用SO_REUSEADDR,尽管如此,TCP还是会禁止一个新的连接建立在一个处于TIME_WAIT状态的五元组(协议、源地址、源端口、目的地址、目的端口)上。

 

 

到不存在的端口的连接请求

         对于UDP来说,目的端口没有在使用,它将产生一个ICMP不可达信息

         对于TCP,则使用复位(RST),例子如下

tcpdump结果如下:

 

 

异常终止一个连接(可通过SO_LINGER设置)

有两个优点:

1、  丢弃任何待发数据,立即发送复位

2、  RST的接收方会区分另一端发来的是RST还是正常终止

需要注意的是,RST报文段不会导致另一端产生任何响应,另一端根本不进行确认。收到RST的一方将终止连接,通知应用层连接复位

例子如下:(-L 0 的意思就是在发送数据后立即发送RST报文段,而不是FIN)

部分tcpdump结果如下:

服务器端则显示:

 

 

检测半打开连接

         一方已关闭或异常终止连接,而另一方却不知道。这种情况下,只要不在连接上传输数据,仍处于连接的一方就不会检测到另一方已发生了异常

         现在模拟这种情况,在客户服务器连接后,服务器主机拔掉网线,这样就不会发送FIN到客户端去,然后重启,再从客户端发送来数据,查看结果:

Tcpdump观察到:


可以看到,之前维护的连接已不复存在,当客户发来数据后,服务器无法识别,随发送RST报文

 

 

同时打开

两个应用程序同时彼此执行打开,发生可能性小

每一端既是服务器又是客户端。

同时打开交换了4个报文段,比正常的三路握手多一个

Tcpdump观察到:

 

 

同时关闭

查看上面对CLOSING状态的解释,加上这里的图示,就能理解了

我们也发现双方都进入了TIME_WAIT状态

 

 

TCP服务器也可以像UDP服务器一样限制本IP口和远端IP

 

关于TCP连接请求队列,即Listen函数的第二个参数,这里提一下

1、在TCP监听的套接字连接队列中还有空间,TCP模块将对新来的请求进行SYN确认并完成连接的建立,然而应用层则需要在三次握手结束后才知道这个新连接。

当客户主动打开成功,而服务器应用进程还不知道这个新连接,客户端就认为服务器已经能接收数据了,假设此时客户端立即发送数据,那么服务器的TCP仅将数据放入缓冲队列

2、这其中有个地方,大多TCP/IP协议如此实现,就是如果服务器连接队列未满,TCP将接收传入的连接请求,但并不让应用层了解到该连接源于何处。

这样一来,当一个新连接传递到服务器应用程序时,TCP的三次握手已经结束!客户主动打开已完全成功。此时若应用进程看到该源地址且不打算给其服务,能做的就是发送FIN或RST,无论如何,客户进程都认为一切正常,因为它的主动打开已完成,并已经向服务器发送过请求。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值