什么是TCP?
TCP(Transmission Control Protocol)是传输控制协议,工做在传输层,具备面向链接、全双工、半关闭、错误检查、将数据打包成段,排序、确认机制、数据恢复,重传、流量控制,滑动窗口、拥塞控制,慢启动和拥塞避免算法的特性,最重要的是TCP协议是可靠的,对于不少重要数据在网络的传递中起到相当重要的做用。linux
先了解一个TCP包头

- 源端口、目标端口:计算机上的进程要和其余进程通讯是要经过计算机端口的,而一个计算机端口某个时刻只能被一个进程占用,因此经过指定源端口和目标端口,就能够知道是哪两个进程须要通讯。源端口、目标端口是用16位表示的,可推算计算机的端口个数为2^16个
- 序列号:表示本报文段所发送数据的第一个字节的编号。在TCP链接中所传送的字节流的每个字节都会按顺序编号。因为序列号由32位表示,因此每2^32个字节,就会出现序列号回绕,再次从 0 开始
- 确认号:表示接收方指望收到发送方下一个报文段的第一个字节数据的编号。也就是告诉发送方:我但愿你(指发送方)下次发送的数据的第一个字节数据的编号为此确认号
- 数据偏移:表示TCP报文段的首部长度,共4位,因为TCP首部包含一个长度可变的选项部分,须要指定这个TCP报文段到底有多长。它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。该字段的单位是32位(即4个字节为计算单位),4位二进制最大表示15,因此数据偏移也就是TCP首部最大60字节
- URG:表示本报文段中发送的数据是否包含紧急数据。后面的紧急指针字段(urgent pointer)只有当URG=1时才有效
- ACK:表示是否前面确认号字段是否有效。只有当ACK=1时,前面的确认号字段才有效。TCP规定,链接创建后,ACK必须为1,带ACK标志的TCP报文段称为确认报文段
- PSH:提示接收端应用程序应该当即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间。若是为1,则表示对方应当当即把数据提交给上层应用,而不是缓存起来,若是应用程序不将接收到的数据读走,就会一直停留在TCP接收缓冲区中
- RST:若是收到一个RST=1的报文,说明与主机的链接出现了严重错误(如主机崩溃),必须释放链接,而后再从新创建链接。或者说明上次发送给主机的数据有问题,主机拒绝响应,带RST标志的TCP报文段称为复位报文段
- SYN:在创建链接时使用,用来同步序号。当SYN=1,ACK=0时,表示这是一个请求创建链接的报文段;当SYN=1,ACK=1时,表示对方赞成创建链接。SYN=1,说明这是一个请求创建链接或赞成创建链接的报文。只有在前两次握手中SYN才置为1,带SYN标志的TCP报文段称为同步报文段
- FIN:表示通知对方本端要关闭链接了,标记数据是否发送完毕。若是FIN=1,即告诉对方:“个人数据已经发送完毕,你能够释放链接了”,带FIN标志的TCP报文段称为结束报文段
- 窗口大小:表示如今容许对方发送的数据量,也就是告诉对方,从本报文段的确认号开始容许对方发送的数据量,达到此值,须要ACK确认后才能再继续传送后面数据,由Window size value * Window size scaling factor(此值在三次握手阶段TCP选项Window scale协商获得)得出此值
- 校验和:提供额外的可靠性
- 紧急指针:标记紧急数据在数据字段中的位置
- 选项部分:其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,选项部分最长为:(2^4-1)*4-20=40字节
在互联网上创建链接

TCP三次握手

过程:
第一次
第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP链接成功)状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。算法
为何要三次握手?有什么意义?
同步链接双方的序列号和确认号并交换 TCP 窗口大小信息,而且确保双方可以相互通讯缓存
sync半链接和accept全链接队列

服务器server从接收到客户端client请求以后从LISTEN状态切换成SYNC-REVD状态,并将客户端链接加入至sync queue队列中,但这个队列的默认值大小通常为128,这时就有可能某些***利用这个阵列来不断的填充你的阵列,达到形成你的服务不可访问的效果 ,咱们能够经过在linux系统上能够经过echo "value" > /proc/sys/net/ipv4/tcp_max_syn_backlog在修改默认值大小,建议value=1024。
同时,从上图咱们能够得知/proc/sys/net/core/somaxconn为完成链接队列大小,也建议调整大小为1024以上。服务器
TCP四次挥手

为何要四次挥手?
TCP协议通讯双方均可以独立关闭本身的通讯通道,也就是半关闭。
client先发送FIN告知对方我已经完成数据发送了,server回复ack来肯定我知道了。这样一个流程,就关闭了client的发送信息通道。可是还能够接收来自server方的数据。
server此时已经知道接收不到client的数据了,可是还能够给它发送数据。若是server也没有啥数据要发送给对方了,server也会以FIN标志位发送一个信息给client,client接到后,也会传递一个ack表示知道了。这样子,双方都完成了关闭。网络
有限状态机

客户端先发送一个FIN给服务端,本身进入了FIN_WAIT_1状态,这时等待接收服务端的报文,该报文会有三种可能:
- 只有服务端的ACK
- 只有服务端的FIN
- 基于服务端的ACK,又有FIN
- 一、只收到服务器的ACK,客户端会进入FIN_WAIT_2状态,后续当收到服务端的FIN时,回应发送一个ACK,会进入到TIME_WAIT状态,这个状态会持续2MSL(TCP报文段在网络中的最大生存时间, RFC 1122标准的建议值是2min).客户端等待2MSL,是为了当最后一个ACK丢失时,能够再发送一次。由于服务端在等待超时后会再发送一个FIN给客户端,进而客户端知道ACK已丢失
- 二、只有服务端的FIN时,回应一个ACK给服务端,进入CLOSING状态,而后接收到服务端的ACK时,进入TIME_WAIT状态
- 三、同时收到服务端的ACK和FIN,直接进入TIME_WAIT状态
- 客户端经过connect系统调用主动与服务器创建链接connect系统调用首先给服务器发送一个同步报文段,使链接转移到SYN_SENT状态
- 此后connect系统调用可能由于以下两个缘由失败返回:
- 一、若是connect链接的目标端口不存在(未被任何进程监听),或者该端口仍被处于TIME_WAIT状态的链接所占用(见后文),则服务器将给客户端发送一个复位报文段,connect调用失败。
- 二、若是目标端口存在,但connect在超时时间内未收到服务器的确认报文段,则connect调用失败。
- connect调用失败将使链接当即返回到初始的CLOSED状态。若是客户端成功收到服务器的同步报文段和确认,则connect调用成功返回,链接转移至ESTABLISHED状态
当客户端执行主动关闭时,它将向服务器发送一个结束报文段,同时链接进入FIN_WAIT_1状态。若此时客户端收到服务器专门用于确认目的的确认报文段,则链接转移至FIN_WAIT_2状态。当客户端处于FIN_WAIT_2状态时,服务器处于CLOSE_WAIT状态,这一对状态是可能发生半关闭的状态。此时若是服务器也关闭链接(发送结束报文段),则客户端将给予确认并进入TIME_WAIT状态
客户端从FIN_WAIT_1状态可能直接进入TIME_WAIT状态(不通过FIN_WAIT_2状态),前提是处于FIN_WAIT_1状态的服务器直接收到带确认信息的结束报文段(而不是先收到确认报文段,再收到结束报文段)
处于FIN_WAIT_2状态的客户端须要等待服务器发送结束报文段,才能转移至TIME_WAIT状态,不然它将一直停留在这个状态。若是不是为了在半关闭状态下继续接收数据,链接长时间地停留在FIN_WAIT_2状态并没有益处。链接停留在FIN_WAIT_2状态的状况可能发生在:客户端执行半关闭后,未等服务器关闭链接就强行退出了。此时客户端链接由内核来接管,可称之为孤儿链接(和孤儿进程相似)tcp
Linux为了防止孤儿链接长时间存留在内核中,定义了两个内核参数:
/proc/sys/net/ipv4/tcp_max_orphans 指定内核能接管的孤儿链接数目
/proc/sys/net/ipv4/tcp_fin_timeout 指定孤儿链接在内核中生存的时间ide