网络编程--TCP协议

时间:2021-07-23 10:25:28
TCP协议是端到端的传输控制协议,之所以是“端到端”的协议,是因为”路由“是由IP协议负责的,TCP协议负责为两个通信端点提供可靠性保证,这个可靠性不是指一个端点发送的数据,另一个端点肯定能收到(这显然是不可能的),而是指, 数据的可靠投递或者故障的可靠通知

TCP的可靠性通过以下方式来保证:
1.超时重传:TCP每发送出一个报文段后,都会启动一个定时器,对目的端传回的确认信息进行确认计时,超时后便重传。
2.确认信号:当TCP收到一个来自TCP的报文段后,便会发送回一个确认信号。
3.检验和:TCP将始终保持首部和数据的检验和,如果收到的报文段的检验和有差错,便将其丢弃,希望发送端超时重传。
4.重新排序:由于IP数据报的达到可能失序,因此TCP将会数据进行重新排序,以正确的顺序交给应用层。
5.丢弃重复:由于IP数据报有可能重复,因此TCP将会丢弃重复的数据。
6.流量控制:TCP连接的两端都有固定大小的缓冲区空间,TCP接受端只允许对端发送本端缓冲区能容纳的数据。

TCP 提供流量控制。在双方进行交互时,会彼此通知自己目前接收缓冲区最多可以接收的数据量(通告窗口),以此确保发送方发送的数据不会溢出接收缓冲区。 

TCP数据包主要包括:
1. SYN包:请求建立连接的数据包
2. ACK包:回应数据包,表示接收到了对方的某个数据包
3. PSH包:正常数据包
4. FIN包:通讯结束包
5. RST包: 重置连接
导致TCP协议发送RST包的原因:
   1)SYN 数据段指定的目的端口处没有接收进程在等待。
   2)TCP协议想放弃一个已经存在的连接。
   3)TCP接收到一个数据段,但是这个数据段所标识的连接不存在。

接收到RST数据段的TCP协议立即将这条连接非正常地断开,并向应用程序报告错误。
6. URG包: 紧急指针

一次完成的TCP通讯包括:建立连接、数据传输、关闭连接
建立连接(三次握手):
1.客户端通过向服务器端发送一个SYN来建立一个主动打开,作为三路握手的一部分。
2.服务器端应当为一个合法的SYN回送一个SYN/ACK。
3.最后,客户端再发送一个ACK。这样就完成了三路握手,并进入了连接建立状态。
数据传输:
1.发送数据端传输PSH数据包
2.接收数据端回复ACK数据包
关闭连接(四次分手)
1. 一端主动关闭连接。向另一端发送FIN包。
2. 接收到FIN包的另一端回应一个ACK数据包。
3. 另一端发送一个FIN包。
4. 接收到FIN包的原发送方发送ACK对它进行确认。

TCP状态转换图:
网络编程--TCP协议
说明( netstat可查看状态):

CLOSED: 初始状态。

LISTEN: 服务器端的某个SOCKET处于监听状态,可以接受连接了。

SYN_RCVD: 服务器接受到了SYN报文。

SYN_SENT: 客户端已发送SYN报文。

ESTABLISHED:连接已经建立了。

FIN_WAIT_1:当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了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连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2个MSL(最大生存时间)值(RFC建议2分钟,Berkeley传统使用30s)后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
TIME_WAIT状态存在两个理由:
1.可靠地实现TCP连接的终止
2.允许老的重复分解在网络中消逝
执行主动关闭的一端都会进入TIME_WAIT状态,Linux下高并发的Squid服务器,TCP TIME_WAIT套接字数量经常达到数万,服务器很容易被拖死。通过修改Linux内核参数,可以减少服务器的TIME_WAIT套接字数量。

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可用状态了。

用下面这条语句可以查看linux服务器的状态:
$ netstat -n | awk '/^tcp/ {++Status[$NF]} END {for(name in Status) print name, Status[name]}'
TIME_WAIT 15
CLOSE_WAIT 6
ESTABLISHED 141

TCP发送数据包过程:

1. 应用程序write数据到发送缓冲区: 如果套接口发送缓冲区容不下应用程序的所有数据,并且应用进程是阻塞的,应用进程将被挂起,直到所有的数据都拷贝到套接口缓冲区。
2.
TCP根据MSS (Maxitum Segment Size)指对数据分段发送。MSS就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的时候往往用MTU(最大传输单元,以太网MTU为1500)值代替(需要减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes)所以往往MSS为1460。通讯双方会根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。

reference:
UNIX网络编程,第一卷