tcpip详解笔记(15) TCP协议连接过程

摘要:
要建立和终止连接,首先查看telnet连接服务器端口80上的数据包捕获:ACK序列号是ISN+1。要终止TCP连接,需要四次握手。当连接的每一端关闭连接时,它会向另一端发送FIN以终止连接。客户端收到RST响应(当UDP连接到不存在的端口时,将发生ICMP端口不可访问错误)。也可以通过发送重置消息段而不是FIN来终止连接。可以通过TCP_KEEPALIVE选项的SO终止连接,以检测半开连接。

TCP是一个面向连接的协议,在发送数据之前,双方必须通过三次握手协议建立连接,而在终止连接的时候执行4次握手协议。

连接建立和终止

先看一下telnet连接服务器80端口的抓包:

tcp

 

上图由wireshark抓取,并显示了TCP状态图(注意:由于网络阻塞,发生了丢包现象,4是对2的重发,而5是对4的响应(同3相同))。

根据上图可以看到建立一个TCP连接的过程为(三次握手的过程):

  1. 客户端向服务器端发送一个SYN请求,同时传送一个初始序列号(ISN);
  2. 服务器发回包含客户端初始序列号的SYN报文段作为应答,同时将ACK序号设置为ISN+1;
  3. 客户端向服务器发送一个ACK确认,ACK序号为ISN+1.

终止一个TCP连接需要4次握手,这是由于TCP的半关闭(当一方调用shutdown关闭连接后,另一端还是可以发送数据,典型的例子为rsh)导致的:TCP连接是全双工的,连接的每一端在关闭连接时都向对方发送一个FIN来终止连接,同时对方会对其进行确认(回复ACK)。通常,都是一方完成主动关闭,另一方来完成被动关闭:

  1. 以上面的抓包为例,客户端向服务器发送了一个FIN(NO. 6);
  2. 服务器端对上面的FIN进行确认(NO. 7),同时向客户端发送一个FIN(这儿其实是两个动作,一个是对上面FIN的ACK,另一个是发送一个FIN,但由于TCP的捎带ACK机制,两者放在一个包里发送了);
  3. 客户端对服务器端的FIN进行确认(NO. 8)。
连接超时

很多情况下,连接可能不会那么顺利,比如发生了丢包就需要重传(NO. 4)。当连接无法建立的时候,会是什么情况呢,看下抓包情况:

tcp_timeout_dump

可以看到,当连接失败时会进行几次重试,但在31s后放弃连接。重试的时间间隔很有规律:1,2, 4, 8, 16,即每次的重试间隔都是上次间隔的2倍。

MSS

最大报文长度(MSS)表示TCP传往另一端的最大块数据的长度。MSS在连接建立时传送给对方,只会出现在SYN报文段中。

MSS让主机限制另一端发送数据报的长度。

TCP状态变迁图

1

TIME_WAIT状态

  1. TIME_WAIT状态也称2MSL等待状态,它是任何报文段被丢弃前在网络内的最长时间。当TCP执行一个主动关闭,并发回一个最后ACK,该连接必须处于TIME_WAIT状态,并停留2MSL时间,这可以让TCP再次发送最后的ACK以防这个ACK丢失。
  2. 在该2MSL期间,该连接占用的端口不可用,任何迟到的报文也会丢弃。通常,为了服务器的快速重启(需要重用端口),可以设置SO_REUSEADDR选项。
  3. 而对于一个被动关闭的TCP连接来说,不会进入TIME_WAIT状态。

FIN_WAIT_2状态表示我们已经发出了FIN,并且对方已对其进行了确认,但对方还未发回FIN以关闭对方连接。只有当对方完成这个关闭,本端才会由FIN_WAIT_2状态进入TIME_WAIT状态。

复位报文段
  1. 当连接到一个不在监听的端口时,客户端回收到一个RST响应(UDP连接到一个不存在的端口时会产生一个ICMP端口不可达的差错)。
  2. 在连接终止时,也可以通过发送一个复位报文段而不是FIN来终止连接,可通过设置SO_LINGER来这么做。
  3. 可通过TCP的SO_KEEPALIVE选项来检测半打开连接,当检测到这种连接时会发送一个RST报文。关于该选项更多的内容参见http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/

SO_LINGER 选项

此选项指定函数close对面向连接的协议如何操作(如TCP)。内核缺省close操作是立即返回,如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。

SO_LINGER选项用来改变此缺省设置。使用如下结构:

struct linger {

int l_onoff; /* 0 = off, nozero = on */

int l_linger; /* linger time */

};

有下列三种情况:

1、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;

2、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT状态;

3、设置 l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直 到(a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)或(b)延迟时间到。此种情况下,应用程序检查close的返回值是非常重要的,如果在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉我们发送的数据(和FIN)已由对方TCP确认,它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的,它将不等待close完成。

注释:l_linger的单位依赖于实现: 4.4BSD假设其单位是时钟滴答(百分之一秒),但Posix.1g规定单位为秒。

 

同时打开

两个应用程序同时执行主动打开的情况是可能的,虽然发生的可能性较低。每一端都发送一个SYN,并传递给对方,且每一端都使用对端所知的端口作为本地端口。例如:

主机a中一应用程序使用7777作为本地端口,并连接到主机b 8888端口做主动打开。

主机b中一应用程序使用8888作为本地端口,并连接到主机a 7777端口做主动打开。

tcp协议在遇到这种情况时,只会打开一条连接。

这个连接的建立过程需要4次数据交换,而一个典型的连接建立只需要3次交换(即3次握手)

但多数伯克利版的tcp/ip实现并不支持同时打开。

2

如果应用程序同时发送FIN,则在发送后会首先进入FIN_WAIT_1状态。在收到对端的FIN后,回复一个ACK,会进入CLOSING状态。在收到对端的ACK后,进入TIME_WAIT状态。这种情况称为同时关闭。

同时关闭也需要有4次报文交换,与典型的关闭相同。

4

参考

《TCP/IP详解》

http://blog.csdn.net/factor2000/article/details/3929816
http://hi.baidu.com/duanzhimin/item/490ca1d970c5bced55347f51

免责声明:文章转载自《tcpip详解笔记(15) TCP协议连接过程》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇telnet 查看端口是否可访问移动硬盘不显示的解决方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Oracle—dblink创建与使用

一、DbLink的创建 1.PLSQL图文方式 2.SQL方式 -- Drop existing database link drop database link LINK_NC_ZS; -- Create database link create database link LINK_NC_ZS connect to ZSFNC identi...

InfluxDB学习之InfluxDB的HTTP API查询操作

    本文我们再来介绍下使用InfluxDB的HTTP API进行数据查询操作的过程。 一、说明 官方文档上介绍说,使用HTTP API进行查询是比较初级的一种方式。推荐使用第三方语言库和客户端管理程序进行查询操作。 二、InfluxDB进行HTTP API查询方法 使用HTTP API在InfluxDB进行查询主要是发送 GET 请求到 Influx...

Oracle查询优化改写--------------------操作多个表

一、union all与空字符串 二、组合相关行 三、in 、exists、inter join 、left join 、right join 、full join 之间的区别   ’inner  join 返回两表相符合的数据    left    join  以左表为主表,左表返回所有的数据,在右表中只返回与左表匹配的数据   right   join...

linux网络 (三):网络测试

1.ping:测试主机之间的网络连通性 ping命令是基于Internet控制消息协议(ICMP协议)来工作的,ICMP报文通过IP协议来发送。 ping发送的是ICMP回显请求,回答的是回显应答报文。 常用选项参数: 1.1.用“-c"选项指定发送的测试报文数目 发送指定的数据包数默认值是3。 ping -c 3 www.baidu.com 1....

NIO:Buffer 详解

如你所见,在NIO中,数据的读写操作始终是与缓冲区相关联的。Channel将数据读入缓冲区,然后我们又从缓冲区访问数据。写数据时,首先将要发送的数据按顺序填入缓冲区。基本上,缓冲区只是一个列表,它的所有元素都是基本数据类型(通常为字节型)。缓冲区是定长的,它不像一些类那样可以扩展容量(例如,List,StringBuffer等)。注意,ByteBuffe...

MongoDB常用操作整理

Mongodb:是一种NoSQL数据库,NoSQL:Not Only SQLSQL: 数据表->JDBC读取->POJO(VO、PO)->控制层转化为JSON数据->客户端 这种转换太麻烦了,如果有直接数据库存放要显示的内容,就能够省略所有需要进行转换的过程。 所以在实际开发中,往往除了关系型数据库之外还要提供一个NoSql数据库,...