本文部分资料和示例援引自以下书籍和博客。在此,感谢原作者的创作,以及所有译者的付出,向他们致敬。
1)《计算机网络第六版》电子工业出版社
2) http://uule.iteye.com/blog/2213562
一、TCP报文格式
上图中有几个字段需要重点介绍下:
(1)、源端口、目标端口:计算机上的进程要和其他进程通信是要通过计算机端口的,而一个计算机端口某个时刻只能被一个进程占用,所以通过指定源端口和目标端口,就可以知道是哪两个进程需要通信。源端口、目标端口是用16位表示的,可推算计算机的端口个数为2^16个
(2)、序列号:表示本报文段所发送数据的第一个字节的编号。在TCP连接中所传送的字节流的每一个字节都会按顺序编号。由于序列号由32位表示,所以每2^32个字节,就会出现序列号回绕,再次从0 开始
(3)、确认号:表示接收方期望收到发送方下一个报文段的第一个字节数据的编号。也就是告诉发送发:我希望你(指发送方)下次发送的数据的第一个字节数据的编号是这个确认号
(4)、数据偏移:表示TCP报文段的首部长度,共4位,由于TCP首部包含一个长度可变的选项部分,需要指定这个TCP报文段到底有多长。它指出TCP 报文段的数据起始处距离TCP 报文段的起始处有多远。该字段的单位是32位(即4个字节为计算单位),4位二进制最大表示15,所以数据偏移也就是TCP首部最大60字节
URG:表示本报文段中发送的数据是否包含紧急数据。后面的紧急指针字段(urgent pointer)只有当URG=1时才有效
(5)、ACK:表示是否前面的确认号字段是否有效。ACK=1,表示有效。只有当ACK=1时,前面的确认号字段才有效。TCP规定,连接建立后,ACK必须为1,带ACK标志的TCP报文段称为确认报文段
(6)、PSH:提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间。如果为1,则表示对方应当立即把数据提交给上层应用,而不是缓存起来,如果应用程序不将接收到的数据读走,就会一直停留在TCP接收缓冲区中
(7)、RST:如果收到一个RST=1的报文,说明与主机的连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。或者说明上次发送给主机的数据有问题,主机拒绝响应,带RST标志的TCP报文段称为复位报文段
(8)、SYN:在建立连接时使用,用来同步序号。当SYN=1,ACK=0时,表示这是一个请求建立连接的报文段;当SYN=1,ACK=1时,表示对方同意建立连接。SYN=1,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中SYN才置为1,带SYN标志的TCP报文段称为同步报文段
(9)、FIN:表示通知对方本端要关闭连接了,标记数据是否发送完毕。如果FIN=1,即告诉对方:“我的数据已经发送完毕,你可以释放连接了”,带FIN标志的TCP报文段称为结束报文段
(10)、窗口大小:表示现在充许对方发送的数据量,也就是告诉对方,从本报文段的确认号开始允许对方发送的数据量
(11)、校验和:提供额外的可靠性
(12)、紧急指针:标记紧急数据在数据字段中的位置
(13)、选项部分:其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,选项部分最长为:(2^4-1)*4-20=40字节
二、TCP三次握手
TCP (Transmission Control Protocol) 传输控制协议
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接
位码即tcp标志位,有6种标示:
SYN(synchronous请求建立联机)
ACK(acknowledgement 同意建立连接)
PSH(push传送)
FIN(finish关闭连接)
RST(reset重置)
URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认顺序号码)
ESTABLISHED 代表传输连接建立,双方进入数据传送状态
需要注意的是:
(A)不要将确认序号ack与标志位中的ACK搞混了。
(B)确认方ack=发起方req+1,两端配对。
所谓三次握手(Three-Way Handshake)即建立TCP连接,是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:
第一次握手:Client将标志位SYN置为1,随机产生一个值seq=j,并将该数据包发送给Server,Client进入SYN-SEND状态,等待Server确认。
第二次握手:Server开始处于CLOSED状态,没有任务连接,然后被动打开,处于LISTEN 侦听状态,等待来自远方TCP端口的连接请求。当Server收到来自Client的数据包后,由数据包中的SYN=1知道Client请求建立连接,然后将标志位SYN和ACK都置为1,ack(number)=j+1,随机产生一个值seq=k,并将该数据包发送给Client以确认其请求连接,Server进入SYN-RCVD状态。
第三次握手:Client收到确认包后,检查ACK是否为1,ack是否为j+1,如果正确,则将标志位ACK置为1,ack(number)=k+1,并将该数据包发送给Server,Server检查ACK是否为1,ack是否为k+1,如果正确,则建立连接成功,Server和Client都进入ESTABLISHED状态,完成三次握手,Server和Client之间就可以传输数据了。
SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV
三、四次挥手
TCP连接释放过程比较复杂,我们任然结合双方状态的改变来阐明连接释放的过程。
第一次挥手:数据传输结束后,通信的双方都可释放连接。现在A和B都处于ESTABLISHED状态,A的应用程序先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FIN置为1,
其序号seq=u,它等于前面已经穿过的数据的最后一个字节的序号加1.这时A进入FIN-WAIT-1(终止等待)状态,等待B的确认,请注意,TCP规定,FIN报文段即使不携带数据,它也消耗一个序号。
第二次挥手:B收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是V,等于B前面已传送过的数据的最后一个字节的序号加1.然后B就进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应通知高层应用程序,因而从A到B这个方向的连接就释放了 ,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发送了,但B若要发送数据,A仍要接受。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一些时间。
A收到来自BD的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段
第三次挥手:若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN=1。现假定B的序号为w(在半关闭状态B可能又发送了一些数据)。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才进入到CLOSE状态。时间MSL叫最长报文段寿命,RFC793建议设为2分钟。但这完全是从工程上来考虑,对于现在的网络,MSL=2分钟可能太长了一些。因此TCP允许不同的实现可根据具体情况使用情况使用更小的MSL值。因此,从A进入到TIME-WAIT状态后,要经过4分钟进入到CLOSED状态,才能开始建立下一个新的连接。当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。