TCP 协议简述
TCP 提供面向有链接的通讯传输,面向有链接是指在传送数据以前必须先创建链接,数据传送完成后要释放链接。
不管哪一方向另外一方发送数据以前,都必须先在双方之间创建一条链接。在TCP/IP协议中,TCP协议提供可靠的链接服务,链接是经过三次握手进行初始化的。
同时因为TCP协议是一种面向链接的、可靠的、基于字节流的运输层通讯协议,TCP是全双工模式,因此须要四次挥手关闭链接。编程
TCP包首部
网络中传输的数据包由两部分组成:一部分是协议所要用到的首部,另外一部分是上一层传过来的数据。首部的结构由协议的具体规范详细定义。在数据包的首部,明确标明了协议应该如何读取数据。反过来讲,看到首部,也就可以了解该协议必要的信息以及所要处理的数据。包首部就像协议的脸。服务器
因此咱们在学习TCP协议以前,首先要知道TCP在网络传输中处于哪一个位置,以及它的协议的规范,下面咱们就看看TCP首部的网络传输起到的做用:
网络
下面的图是TCP头部的规范定义,它定义了TCP协议如何读取和解析数据:
socket
TCP首部承载这TCP协议须要的各项信息,下面咱们来分析一下:tcp
TCP端口号:
TCP的链接是须要四个要素肯定惟一一个链接:
(源IP,源端口号)+ (目地IP,目的端口号)
因此TCP首部预留了两个16位做为端口号的存储,而IP地址由上一层IP协议负责传递
源端口号和目地端口各占16位两个字节,也就是端口的范围是2^16=65535
另外1024如下是系统保留的,从1024-65535是用户使用的端口范围学习
TCP的序号和确认号:
32位序号 seq:Sequence number 缩写seq ,TCP通讯过程当中某一个传输方向上的字节流的每一个字节的序号,经过这个来确认发送的数据有序,好比如今序列号为1000,发送了1000,下一个序列号就是2000。
32位确认号 ack:Acknowledge number 缩写ack,TCP对上一次seq序号作出的确认号,用来响应TCP报文段,给收到的TCP报文段的序号seq加1。.net
TCP的标志位
每一个TCP段都有一个目的,这是借助于TCP标志位选项来肯定的,容许发送方或接收方指定哪些标志应该被使用,以便段被另外一端正确处理。
用的最普遍的标志是 SYN,ACK 和 FIN,用于创建链接,确认成功的段传输,最后终止链接。指针
TCP 三次握手创建链接
所谓三次握手(Three-way Handshake),是指创建一个 TCP 链接时,须要客户端和服务器总共发送3个报文。code
三次握手的目的是链接服务器指定端口,创建 TCP 链接,并同步链接双方的序列号和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手。server
三次握手过程的示意图以下:
第一次握手:
客户端将TCP报文标志位SYN置为1,随机产生一个序号值seq=J,保存在TCP首部的序列号(Sequence Number)字段里,指明客户端打算链接的服务器的端口,并将该数据包发送给服务器端,发送完毕后,客户端进入SYN_SENT
状态,等待服务器端确认。
第二次握手:
服务器端收到数据包后由标志位SYN=1知道客户端请求创建链接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=J+1,随机产生一个序号值seq=K,并将该数据包发送给客户端以确认链接请求,服务器端进入SYN_RCVD
状态。
第三次握手:
客户端收到确认后,检查ack是否为J+1,ACK是否为1,若是正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,若是正确则链接创建成功,客户端和服务器端进入ESTABLISHED
状态,完成三次握手,随后客户端与服务器端之间能够开始传输数据了。
注意:咱们上面写的ack和ACK,不是同一个概念:
小写的ack表明的是头部的确认号Acknowledge number, 缩写ack,是对上一个包的序号进行确认的号,ack=seq+1。
大写的ACK,则是咱们上面说的TCP首部的标志位,用于标志的TCP包是否对上一个包进行了确认操做,若是确认了,则把ACK标志位设置成1。
下面我本身作实验,开一个HTTP服务,监听80端口,而后使用Tcpdump命令抓包,看一下TCP三次握手的过程:
1. sudo tcpdump -n -t -S -i enp0s3 port 80 4. 第一次握手,标志位Flags=S 5. IP 10.0.2.2.51323 > 10.0.2.15.80: Flags [S], seq 84689409, win 65535, options [mss 1460], length 0 6. 第二次握手,标志位Flags=[S.] 7. IP 10.0.2.15.80 > 10.0.2.2.51323: Flags [S.], seq 1893430205, ack 84689410, win 64240, options [mss 1460], length 0 8. 第三次握手,标志位Flags=[.] 9. IP 10.0.2.2.51323 > 10.0.2.15.80: Flags [.], ack 1893430206, win 65535, length 0 10. 创建链接后,客户端发送http请求 11. IP 10.0.2.2.51321 > 10.0.2.15.80: Flags [P.], seq 1:753, ack 1, win 65535, length 752: HTTP: GET / HTTP/1.1
tcpdump命令解析一下: -i : 指定抓包的网卡是enp0s3 -n: 把域名转成IP显示 -t: 不显示时间 -S: 序列号使用绝对数值,不指定-S的话,序列号会使用相对的数值 port: 指定监听端口是80 host:指定监听的主机名
咱们看下实战中TCP的三次握手过程:
至此,三次握手完成,一个TCP链接创建完成,接下来就是双端传输数据了
为何须要三次握手?
咱们假设client发出的第一个链接请求报文段并无丢失,而是在某个网络结点长时间的滞留了,以至延误到链接释放之后的某个时间才到达server。
原本这是一个早已失效的报文段。但server收到此失效的链接请求报文段后,就误认为是client再次发出的一个新的链接请求。因而就向client发出确认报文段,赞成创建链接。
假设不采用“三次握手”,那么只要server发出确认,新的链接就创建了。因为如今client并无发出创建链接的请求,所以不会理睬server的确认,也不会向server发送数据。但server却觉得新的运输链接已经创建,并一直等待client发来数据。这样,server的不少资源就白白浪费掉了。
因此,采用“三次握手”的办法能够防止上述现象发生。例如刚才那种状况,client不会向server的确认发出确认。server因为收不到确认,就知道client并无要求创建链接。
TCP 三次握手跟现实生活中的人与人打电话是很相似的:
三次握手:
“喂,你听获得吗?” “我听获得呀,你听获得我吗?” “我能听到你,今天 balabala……”
TCP 四次挥手关闭链接
四次挥手即终止TCP链接,就是指断开一个TCP链接时,须要客户端和服务端总共发送4个包以确认链接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
因为TCP链接是全双工的,所以,每一个方向都必需要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的链接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,可是在这个TCP链接上仍然可以发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另外一方则执行被动关闭。
四次挥手过程的示意图以下:
挥手请求能够是Client端,也能够是Server端发起的,咱们假设是Client端发起:
FIN_WAIT_1
状态,这表示Client端没有数据要发送给Server端了。FIN_WAIT_2
状态,Server端告诉Client端,我确认并赞成你的关闭请求。LAST_ACK
状态。TIME_WAIT
状态。Server端收到Client端的ACK报文段之后,就关闭链接。此时,Client端等待2MSL的时间后依然没有收到回复,则证实Server端已正常关闭,那好,Client端也能够关闭链接了。为何链接的时候是三次握手,关闭的时候倒是四次握手?
创建链接时由于当Server端收到Client端的SYN链接请求报文后,能够直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。因此创建链接只须要三次握手。
因为TCP协议是一种面向链接的、可靠的、基于字节流的运输层通讯协议,TCP是全双工模式。
这就意味着,关闭链接时,当Client端发出FIN报文段时,只是表示Client端告诉Server端数据已经发送完毕了。当Server端收到FIN报文并返回ACK报文段,表示它已经知道Client端没有数据发送了,可是Server端仍是能够发送数据到Client端的,因此Server极可能并不会当即关闭SOCKET,直到Server端把数据也发送完毕。
当Server端也发送了FIN报文段时,这个时候就表示Server端也没有数据要发送了,就会告诉Client端,我也没有数据要发送了,以后彼此就会愉快的中断此次TCP链接。
为何要等待2MSL?
MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。
有如下两个缘由:
第一点:保证TCP协议的全双工链接可以可靠关闭:
因为IP协议的不可靠性或者是其它网络缘由,致使了Server端没有收到Client端的ACK报文,那么Server端就会在超时以后从新发送FIN,若是此时Client端的链接已经关闭处于CLOESD
状态,那么重发的FIN就找不到对应的链接了,从而致使链接错乱,因此,Client端发送完最后的ACK不能直接进入CLOSED
状态,而要保持TIME_WAIT
,当再次收到FIN的收,可以保证对方收到ACK,最后正确关闭链接。
第二点:保证此次链接的重复数据段从网络中消失
若是Client端发送最后的ACK直接进入CLOSED
状态,而后又再向Server端发起一个新链接,这时不能保证新链接的与刚关闭的链接的端口号是不一样的,也就是新链接和老链接的端口号可能同样了,那么就可能出现问题:若是前一次的链接某些数据滞留在网络中,这些延迟数据在创建新链接后到达Client端,因为新老链接的端口号和IP都同样,TCP协议就认为延迟数据是属于新链接的,新链接就会接收到脏数据,这样就会致使数据包混乱。因此TCP链接须要在TIME_WAIT状态等待2倍MSL,才能保证本次链接的全部数据在网络中消失。
本文转自csdn码农富哥