版权声明:本文为作者原创文章,可以随意转载,但必须在明确位置表明出处!!!
之前有一篇文章介绍了http协议「初识http协议」, http协议协议是基于tcp协议的,所以作者觉得有必要针对tcp协议做一个介绍,希望各位读者能够静下心来认真阅读,也可以自己去看看TCP/IP协议详解这本书,一定要让自己成为那20%的人。
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,对TCP协议的文章网上已经很成熟了,今天我只是想总结一下知识,加深印象,所谓好记心不如烂笔头麻。
TCP/IP分层结构
TCP/IP协议栈主要分为4层:应用层,传输层,网络层,数据链路层
- 应用层
应用层负责处理特定的应用程序细节,像远程登陆,FTP传输,SMTP邮件传输,SNMP简单网络管理。
- 传输层
运输层主要提供两台主机之间端到端的通信,在TCP/IP协议族中,TCP和UPD是两种截然不同的传输协议,TCP(传输控制协议)为主机之间提供可靠传输,它把从应用层得到的数据分成适当的数据包交给下面的(IP)网络层,确定接收到的分组,设置发送最后确认分组的超时时钟等,因运输层提供了高可靠性的端到端的通信,应用层就不需要再去关注这些细节,而UDP(用户数据协议)提供的是不可能性的传输,它只负责从一台主机发送到另一台主机,并不保证此数据一定到达另一端主机,任何必需的可靠性必须由应用层来提供。
- 网络层
网络层主要处理分组在网络中的活动,网络层协议包涵IP,ICMP,IGMP协议。
- 链路层
链路层组要包涵网卡驱动程序,它处理和电缆或者其它传输介质的物理接口细节。链路层的主要目的有三个
- 为IP模块接收和发送IP数据报
- 为ARP模块发送ARP请求和接收ARP应答
- 为RARP模块接收RARP应答和发送RARP请求
所谓的协议就是通信双方都需要遵守的规则,这样才能明白对方要表达什么,就像两个人打电话一样,A说的是重庆话,B说的是广东话,这两人打电话肯定不知道对方说的是什么,这就叫他们没有遵守协议,若是都让他们说普通话这样俩儿人就都能听懂对方说的是什么意识了,普通话这里就相当于协议大家都要遵守。下面我将结合Wireshark抓包工具来分析TCP/IP协议
封装
数据进入协议栈的封装过程
当经过以太网层的封装后,就要通过网线或者其它传输介质把此封装好的数据报文发送到另一端去,另一段收到数据报后最先接触的是以太网层也就是我们的数据链路层协议,该层协议复制把以太网首部解析掉,让后把解析后的数据报上送到IP层,IP层把IP首部解析掉,然后上传到TCP层,依次类推每层协议解析其首部并判断其首部中的协议标识以确定接收数据的上层协议,然后上送到他的上一层。这就是封层结构的好处之一,每层协议只做自己的事,不是自己的事就交给别人去做。
TCP报文格式
Wireshark的抓包结果
原端口/目的端口(16bit):
我们都知道网络之前的通信是不通主机之间的通信,就windows系统而言通过查看任务管理器我们可以知道一台主机有许多进程,当我们发送数据时怎么知道要发送到对方主机那个进程里呢,所以这就是端口号的作用,在TCP报文中包涵了源端口/目的端口,源端口标识了发送进程,目的端口标识了接收方进程。在此报文中我们的源端口号是0x8572 = 34162, 目的端口是0x01bb = 443如下图所示
序列号(32bit)
Sequence Number这个是发送序列号,用来标识从源端向目的端发送的数据字节流,它表示在这个报文端中的第一个数据字节的顺序号,系列好是32位的无符号类型,序号表达达到2^32 - 1后又从0开始, 当建立一个新的连接时,SYN标志为1,系列号将由主机随机选择一个顺序号ISN(Initial Sequence Number)。此报文中的序列号是0x9e546d6b早已超过了2^32 - 1 所以这里的序列号为0,如下图
确认号(32bit)
Acknowledgment Number它包涵了发送确认一端所期望收到的下一个顺序号。因此确认序列号应当是上次成功接收到数据的顺序号加1。只有ACK标志为1时确认序号字段才有效。TCP为应用层提供全双工服务,这意味着数据能在两个方向上独立的进行传输,因此连接的两断必须要保证每个方向上的传输数据顺序。
偏移(4bit)
这里的偏移实际指的是TCP首部的长度,它用来表明TCP首部中32bit字的数目,通过它可以知道一个TCP包它的用户数据从哪里开始,这个字段占4bit,若此字段的值为1000,则说明TCP首部的长度是8 * 4 = 32字节,所以TCP首部的最大长度是该字段的值为1111 = 15, 15 * 4 =60字节。此报文我们的偏移量在0x80中,又因它占4bit,0x80等于二进制的1000 0000 所以我们的偏移量是 1000 = 8,所以我们的TCP报文头为8 * 4 = 32字节。
Reserved(6bit)
目前没有使用,它的值都为0
标志(6bit)
在TCP首部中有6个标志比特,他们中的多个可同时被置为1
- URG(Urgent Pointer Field Significant):紧急指针标志,用来保证TCP连接不被中断,并且督促中间设备尽快处理这些数据
- ACK(Acknowledgement Field Signigicant):确认号字段,该字段为1时表示应答字段有效,即TCP应答号将包含在TCP报文中。
- PSH(Push Function): 推送功能,所谓推送功能指的是接收端在接收到数据后立即推送给应用程序,而不是在缓冲区中排队。
- RST(Reset the connection): 重置连接,不过一搬表示断开一个连接,如下图,主机192.168.3.27 访问主机211.150.84.8;但主机84.8并没有监听对于端口,这时它会向主机3.27发送一个RST位置的TCP包断开连接。
- SYN(Synchronize sequence numbers):同步序列号,用来发起一个连接请求
- FIN(No more data from sender):表示发送端发送任务已经完成(既断开连接)
窗口大小(16bit)
表示源主机最大能接收多少字节。
校验和(16bit)
包含TCP首部和TCP数据段,这是一个强制性的字段,一定是由发送端计算和存储,由接收端进行验证
紧急指针(16bit)
只有当URG标志置为1时该字段才有效,紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一段发送紧急数据的一种方式。
TCP选项
至少1个字节的可变长字段,标识哪个选项有效。Kind=0:选项表结束, Kind=1:无操作, Kind=2:最大报文段长度,Kind=3:窗口扩大因子, Kind=8:时间戳。
TCP的三次握手和四次挥手
整个过程如下图所示
TCP的三次握手
TCP的三次握手过程如下图所示,我们通过数据包来分析一下握手过程是不是和图中所画一致。
第一次握手
主机192.168.3.27向主机111.13.100.91发起连接请求,可以看在这时的SYN被置为1了,序列号Seq = 0,如下图
第二次握手
主机111.13.100.91应答主机192.168.3.27,可以看到这个时候的应答包含了SYN,ACK,ACK = Seq + 1 = 1, 这里的Seq是第一次握手发起请求的Seq值,并不是下图报文中红框表示的Seq值。
第三次握手
主机192.168.3.27应答主机111.13.100.91,可以看到这个时候的应答包是ACK, ACK = Seq + 1 = 1,这里的Seq是第二次握手主机111.13.100.91产生的序列值
在回头看看我们的svr4.1037主机和bsdi.discard之前的连接建立是不是和我们的报文分析的一致,第一次握手 SYN Seq = 1415531521; 第二次握手 SYN ack = 1415531521 + 1 = 1415531522 Seq = 1823083521;第三次握手 ack = 1823083521 + 1 = 1823083522;到这里就可以看出此过程和我们的报文分析是一致的。
可以看到三次握手后确定了双方包的序列号,最大接收数据的大小以及MSS(Maximum Segment Size)最大分片大小 MSS = MTU - IP头部长度 - TCP头部长度,MTU最大传输单元一班为1500字节,若TCP/IP报文都不带选项时MSS = 1500 - 20 - 20 = 1460,MSS的意思是最大分片大小,这里若是1460的话,那么若是应用程序发送大于1460个字节那么超过1460个字节数会分片为下一个包,下图是应用层发送4096个字节。
由于数据大小是4096个字节,所以用了三次进行传递(1448 + 1448 + 1200)。细心的人会问为什么每次传送的最大数据大小不是1460个字节呢?因为这里的TCP携带可选项,TCP头长度 = 20 + 12(可选选项大小) = 32字节。 这样能传输的最大数据为:1500 - 20 - 32 = 1448个字节。
TCP四次挥手
第一次挥手
将设客户端首先发起断开连接,那么客户端回想服务端发送FIN置为1的TCP包,请求断开连接,意思就是我要断开和你的连接了,但是如果你还有数据没有发送完给我你不必立即关闭连接。
第二次挥手
服务端收到客户端的断开连接请求立即响应一个ACK报文,意思是告诉客户端你发起的断开连接请求我已经收到了,但是我还没有准备好,你在等一会,这个时候服务器端可能还有数据要发送给客户端,也可能正在准备释放资源。这个时候客户端进入FIN_WAIT状态,继续等待服务端的FIN报文。
第三次挥手
服务端确认已经发往客户端的数据已经发送完成,则向客户端发送FIN报文,告诉客户端我已准备好关闭连接了。
第四次挥手
客户端收到服务端的FIN报文后就知道可以关闭连接了,当时它还是不确定,怕服务端还是不知道我要关闭连接了,所以发送一个ACK包后进入TIME_WAIT状态,如果服务端没有收到ACK那么服务端则可以发起重传,如果收到了ACK报文,客户端等待2MSL后已然没有收到回复,则证明服务端已经正常关闭了,那我也就可以关闭连接了。