负载均衡中的TCP协议
熟悉TCP协议的一些工作原理,是进行负载均衡故障排查的基础。因此,本文将带领大家了解有关TCP/IP协议的一些基础,并了解在增加了负载均衡之后,TCP/IP协议交互发生的一些变化。如果你已经对TCP协议了如指掌,你可以无视本文。
在负载均衡中,TCP可以说是最重要的协议了。因为,我们经常遇到的一些应用层协议,如:HTTP、HTTPS、FTP,全都跑在TCP协议之上。UDP虽然也很重要,但其协议交互相较于TCP来说,要简单得多。因此,让我们先来看看TCP的交互过程。
学习TCP协议中的几个关键
首先,我们必须明白,互联网协议的分层设计,是非常非常伟大的构想。如果不是这样,互联网根本不可能发展到今天如此复杂、如此丰富的境地。因此,我们要了解的第一点就是:TCP协议是运行在IP协议层之上的,是承载其他应用协议的基础。
其次,在平时进行负载均衡的故障排查时,在排除网络连通性问题之后,我们首先要搞清楚的,就是客户端与服务器之间是否成功建立了TCP连接。因此,第二个关键点:TCP协议是基于连接的协议——这一点是TCP与UDP的根本区别。也是导致TCP复杂性的根本原因。
最后,TCP协议看起来简单,了解了TCP三次握手建立连接以及连接拆除的两个FIN过程后,对于大多数TCP方面的故障,你都能从分析TCP交互过程中找到故障排查的思路。但是,TCP协议实际上很复杂。单单是与TCP协议相关的RFC就有几十个。因此,第三个关键点:还是那句老话,你对TCP协议的理解有多深,决定了你分析或解决问题的水平。
TCP的数据包结构
在了解TCP协议交换过程之前,我们首先需要简单的了解一下TCP数据帧的帧结构。
首先,TCP协议是封装在IP协议中的。剥去IP协议头部,紧接着就是TCP的头了。
在TCP协议中,有几个关键的字段是我们一定要搞明白的。搞不懂这几个字段的含义,那我们就无法解读采用类似wireshark这样的抓包工具捕获的数据了。
1) Source port number,发送这个数据包的客户端/服务器使用的端口(小心点!我可没说是客户端或服务器服务器端口啊,是发送该数据包所使用的端口)
2) Destination port number,接收这个数据包的客户端/服务器使用的端口
3) Sequence number和Acknowledgment number,用于确认是否收到了对方发送的数据。一般简称SYN number和ACK number
4) 数据包标记位,表示当前数据包类型的,一般情况下,我们经常会碰到以下几种:SYN、SYN+ACK、ACK、FIN、FIN+ACK、RST。
TCP状态转换图
简单的说,TCP建立连接的过程就像是打电话,你需要先拿起电话(系统获取Socket),拨个号码(发出建立连接的请求),对方接听(确认连接建立),当然,实际的过程会稍微有些区别。
下面这张图,摘自大名鼎鼎的 W. Richard Steven的互联网圣经《TCP/IP Illustrated, Volume 1: The Protocols》的英文版。国内有这本书的中文版,名叫《TCP/IP协议详解,卷1》。但是,如果你的英语还可以的话,非常建议你直接阅读英文版。因为中文版的翻译中充满了错误和误导。仅仅是这张TCP状态转换图就有多处翻译错误或不当的地方。有兴趣的童鞋可以分析比较一下,看看到底有哪些东西翻译得有问题。
想看懂上面这张TCP状态转换图,以下几个关键点是一定要搞清楚并铭记在心的:
1) 图下面的图例是一定要仔细阅读的:实线箭头代表客户端状态转换;虚线箭头代表服务器端状态转换。
2) recv表示客户端/服务器在收到该数据包时发生的状态变迁;send表示在客户端/服务器在发出某种数据包后发生状态变迁。
3) 整个TCP状态转换中最关键的有两个部分:连接建立和连接拆除。
4) TCP连接建立的过程俗称三次握手,是由客户端主动发起的;连接拆除可以由客户端或服务器主动发起,主动发起方在发出关闭连接的请求后,进入Active Close状态,而接收到关闭连接请求的一方则进入Passive close状态。
5) 主动发起TCP连接关闭的一方(进入Active close状态)会在双方确认关闭连接后,进入TIME_WAIT状态,并在2MSL时间之后,才能将连接删除。
了解这张图,我们才能更好的读懂我们从Linux或windows上看到的当前系统TCP/UDP连接状态。下面的例子是我从自己笔记本上抓到的当前系统连接状态,让我们看看当前我的笔记本上对外的连接都有哪些,分别是什么状态:
- C:\>netstat -an
- 活动连接
- 协议 本地地址 外部地址 状态
- TCP 127.0.0.1:5354 0.0.0.0:0 LISTENING
- TCP 127.0.0.1:5354 127.0.0.1:49167 ESTABLISHED
- TCP 127.0.0.1:27015 0.0.0.0:0 LISTENING
- TCP 127.0.0.1:49167 127.0.0.1:5354 ESTABLISHED
- TCP 127.0.0.1:49206 0.0.0.0:0 LISTENING
- TCP 127.0.0.1:52056 127.0.0.1:52057 ESTABLISHED
- TCP 127.0.0.1:52057 127.0.0.1:52056 ESTABLISHED
- TCP 127.0.0.1:52059 127.0.0.1:52060 ESTABLISHED
- TCP 127.0.0.1:52060 127.0.0.1:52059 ESTABLISHED
- TCP 192.168.0.25:139 0.0.0.0:0 LISTENING
- TCP 192.168.0.25:52444 119.147.45.221:443 ESTABLISHED
- TCP 192.168.0.25:53291 119.147.45.221:443 CLOSE_WAIT
- TCP 192.168.0.25:53556 128.241.217.208:80 TIME_WAIT
- TCP 192.168.0.25:53557 128.241.217.193:80 TIME_WAIT
- TCP 192.168.0.25:53568 172.16.16.8:80 SYN_SENT
首先,我的笔记本上有几个侦听状态的端口(状态为LISTENING),比如:192.168.0.25上的139端口。其它客户端可以尝试与我笔记本上的139端口建立连接。
其次,有几个已经建立的TCP连接(状态为ESTABLISHED),从192.168.0.25的52444端口到119.147.45.221的443端口。还有几个正在关闭的连接,比如:从192.168.0.25的53291端口到119.147.45.221的443端口。
另外,大家可能还注意到,有两种不同的连接关闭的状态:CLOSE_WAIT和TIME_WAIT。大家可以先参照上面的TCP状态转换图来看看这两种状态有什么区别。后文中还有说明。
最后,还有一个正在建立的连接(SYN_SENT),从192.168.0.25的53568端口到172.16.16.8的80端口,发出了SYN,正在等待SYN ACK。
TCP连接建立及拆除过程
上图是一个典型的TCP连接建立和拆除的过程,此外,我图中增加了TCP状态转换的过程,大家可以与前面的TCP状态转换图对照着看。从这张图中,我们可以更清楚的看到客户端与服务器连接建立和拆除的各阶段。
TCP连接建立的过程:
1)客户端打开一个连接,并向目标服务器发送SYN,进入SYN_SENT状态;
2)服务器收到客户端的SYN,并向客户端返回SYN+ACK,进入SYN_RCVD状态;
3)客户端向服务器返回ACK,确认连接建立,此时客户端进入ESTABLISHED状态;服务器收到该ACK包后,也进入ESTABLISHED状态。
此时,客户端和服务器完成TCP三次握手,建立TCP连接。需要注意的是,在这整个交互的过程中,客户端需要使用相同的IP地址及端口号,与相同的服务器地址和端口号进行交互。
在客户端/服务器完成数据的传输之后,任何一方都可主动关闭连接。
TCP连接拆除的过程:
1)客户端(或服务器)向对端发送FIN,要求主动关闭该TCP连接。主动关闭连接方进入FIN1_WAIT状态(在这里,我们简称主动关闭连接的一方为A)。
2)对端(被动关闭连接的一方,我们简称P)收到FIN后,会发送ACK确认,进入CLOSE_WAIT状态;而A收到该ACK后,会进入FIN2_WAIT状态。
3)如果P确认数据已经发送完毕,连接可以关闭,那么P也会向A发送FIN,此时,P进入LAST_ACK状态。
4)A收到FIN之后,会进入TIME_WAIT状态,并向P发送ACK确认收到该FIN包。此时,A上的该连接并不会直接删除,而是等待2MSL时间后才会删除该连接。
5)P收到最后的一个ACK后,确认连接关闭,进入CLOSED状态,从本地会话表中删除该会话信息。
通常情况下,我们需要仔细的了解TCP建立会话的过程中每个数据包的作用和意义,而对于TCP连接拆除的过程,一般都不会太过于关注。但是,如果你在某些情况下需要进行负载均衡的性能测试,那么,这个过程就完全不能忽略了。至于原因,我们以后再说。
Telnet——验证TCP连接的简单工具
通常情况下,我们可以利用Telnet来验证TCP连接是否能够成功建立,即客户端与服务器是否能够成功的进行三次握手进入TCP的ESTABLISHED状态。
默认情况下,Linux、Windows XP等操作系统默认都会有Telnet这个程序,但是对于Windows 7,你要小心一点,这个程序变成了选装程序了。
在命令行下输入telnet x.x.x.x port,(x.x.x.x代表你要连接的地址,port代表你要连接的服务器的端口号)系统会提示正在连接到x.x.x.x地址。
如果telnet能够成功的建立TCP连接,命令行提示符会清空,或者显示一些文字。命令行窗口可以允许你输入一些命令与远端进行交互。
如果telnet无法成功的建立TCP连接,在经过几次SYN重传之后,系统会提示“无法打开到主机的连接”
本文转自 virtualadc 51CTO博客,原文链接:http://blog.51cto.com/virtualadc/796001