前言:
- TCP协议是计算机的基础,他本身是一个非常非常复杂的协议。
- 本文只是蜻蜓点水,将从网络基础以及TCP的相关概念介绍开始,之后再将三次握手,四次挥手这些内容来阐述。
- 最后介绍一些常见问题,并给出解答。
网络分层
在实际的网络中,我们是四层网络结构:
网络传输层
网络传输层负责最底层的底层链路连接。两台主机之间进行互联,基于网线的物理硬件上的协议。在这个侧面,主机与主机之间只认得硬件mac编码。并不认识IP。
网络层
IP就是在网络层出现的,就像网络上,每个机器的地址。网络层可以理解为快递,它的职能就是根据地址(IP),把东西从一个地方运送到另一个地方
传输层
传输层相比于网络层最大的不同就是引入了端口的概念。网络层只管发送地址和目的地址。但是发送主机上有可能有多个程序和同一个接收主机进行传输数据,怎么区分这多个程序呢?就引入了端口的概念。(发送IP地址,发送端口,接收IP地址,接收端口)四元组标示了一个主机的程序到另一个主机程序的唯一标示。传输层的职能,就是维护这个四元组。
其实传输层还有一个职能是定义发送方和接收方基本处理包的行为。上面说到网络层就相当于邮件运输工,它只负责把一包东西从一个地方放到另外一个地方,但是,这包东西是否送达了,送达之后接收方又有什么行为。这些都可以在传输层进行定义。注意,这里说的是可以,你也可以在传输层布不管这些,只做简单的基本封装四元组,比如UDP
应用层
指定到主机端口了,接下来就是应用层干活了,可以传文件,传文本。应用层就是实际上对具体的程序之间的交互功能进行定义的层。
TCP协议
TCP(Transmission Control Protocol) 传输控制协议.TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接。
协议结构如下:
这里就不全部都讲了,主介绍一部分:
Source Port和Destination Port
这两个字段表示发送地址和目的地址的端口号,发送地址的IP和目的地址的IP是在IP协议头中
Sequence number(顺序号码) Acknowledge number(确认号码)
TCP的任意一端(不管是客户端还是服务端),可以发送数据,也可以接受数据。那么发送序列号就是Seqence Number,接受序列号就是Acknowledgement Number。
其次,序列号是用来标志包的顺序的。网络中包由于网络问题,接受到的并不是按顺序到达的,接受端可以根据这个序列号来进行组装。
比如:
给服务端发送8000字节的数据,顺序号从10000开始,分成4个包发送,那么它们的标识就是12000,14000,16000,18000。
如下图所示,由于网络问题,最先到达的是序号为14000,但是服务端可以根据这些包的序号进行拼接,拼成完整的包。
Data offset和Reserved
由于tcp头可能是不固定大小的(因为存在可选字段),所以需要有这个值来表示当前这个包的tcp头有多大。
Reserved就是保留字段
位码TCP标志位
就是下图中的红框的内容
SYN(synchronous建立联机):建立连接,发送一方告知另外一方,请求建立连接
ACK(acknowledgement 确认):该包中有回复信息
PSH(push传送):该包中有传输信息
FIN(finish结束):结束位,发送一方告知另外一方,请求中断连接
RST(reset重置):重置位,这个包是用来要对方重置连接
URG(urgent紧急):紧急位,已经建议弃用
握手与挥手:
有了上面的基础知识,相信下面的三次挥手和四次握手理解起来也不会费劲的。
下图是三次握手的过程:
三次握手过程说明:
TCP服务器进程先创建传输控制块TCB(存储了每一个连接中的一些重要信息,如:TCP连接表,到发送和接收缓存的指针,到重传队列的指针,当前的发送和接收序号,等),准备接受客户进程的连接请求。然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求。如有,即作出相应。
第一次握手:
客户端Client向服务端Server发起建立连接请求。客户端会发送位码SYN(请求建立连接),以及序列号x(seq=x),TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。
第二次握手:
服务端Server接受到请求报文段之后。如同意建立连接,则向客户端发送确认消息。在确认报文段中应把SYN位和ACK位都置1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。注意。这个报文段也不能携带数据,但同样要消耗掉一个序号。这时服务端进程进入SYN-RCVD(同步收到)状态。
第三次握手:
客户端Client收到服务端Server的确认后,还要向服务端给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1。这时,TCP连接已经建立,客户端进入ESTABLISHED(已连接状态)。当服务端收到客户端的确认之后,也进入ESTABLISHED状态。
总结:
客户端在三次握手中,状态的转变是:CLOSED->SYN_SEND->ESTABLISHED
服务端在三次握手中,状态的转变是:CLOSED->LISTENED->SYN_RCVD->ESTABLISHED
问题一:为什么不可以两次握手,为什么客户端还要再发送一次确认?
答:消除旧有连接请求的SYN消息对新连接的干扰,同步连接双方的序列号和确认号并交换TCP 窗口大小信息。
比如说这种异常情况:客户端发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达服务端。本来这是一个早已失效的报文段。但服务端收到此失效的连接请求报文段后,就误以为是客户端又发出一次新的连接请求。于是就向客户端发出确认报文段,同意建立连接。假定不采用三次握手,那么只要服务端发出确认,新的连接就建立了。
问题二:什么是SYN攻击?如何检测它?
在三次握手过程中,服务器发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect).此时服务器处于Syn_RECV状态.当收到ACK后,服务器转入ESTABLISHED状态.
Syn攻击就是 攻击客户端 在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直 至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
Syn攻击是一个典型的DDOS攻击。检测SYN攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击.在Linux下可以如下命令检测是否被Syn攻击:
netstat -n -p TCP | grep SYN_RECV
一般较新的TCP/IP协议栈都对这一过程进行修正来防范Syn攻击,修改tcp协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等.
但是不能完全防范syn攻击。
四次挥手过程说明:
在客户端和服务端已经建立连接的情况下,需要四次挥手来断开连接。如下图所示:
第一次挥手:
客户端发送一个FIN=1的报文段和顺序号为u(seq=u)的请求关闭消息(客户端没有消息要发给你了,我准备关闭连接了,但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK)。客户端进入FIN-WAIT-1状态,等待服务端的FIN报文段。
第二次挥手:
服务端收到客户端的FIN报文段,会发送一个确认报文段ACK=1,以及确认序列号ack=u+1,还有自己的序列号seq=v(告诉客户端,我已经收到你的请求关闭消息,但是我还没准备好,请继续等待),服务单进入CLOSE-WAIT阶段,此时服务端还未关闭。
第三次挥手:
服务端向客户端发送FIN=1报文段(告诉Client端,好了,我这边数据发完了,准备好关闭连接了),ACK=1,序列号seq=w,ack=u+1,服务端进入LAST-ACK状态
第四次挥手:
客户端收到服务端发来的FIN=1报文段,给服务端发送ACK=1报文段,序列号seq=u+1,ack=w+1的消息。这时客户端就可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传 ,客户端等待了2MSL后依然没有收到回复,客户端关闭。服务端也关闭。
总结:
客户端在四次挥手中的状态变化是:ESTABLISHED -> FIN-WAITED-1 -> FIN-WAITED-2 -> TIME-WAITED -> CLOSED
服务单在四次挥手中的状态变化是:ESTABLISHED -> CLOSE-WAITED LAST-ACK -> CLOSED
问题一:为什么在第四次回收后会有2个MSL的延时?
首先了解MSL,Maximum Segment Lifetime,最大报文生存时间,2个MSL是报文段发送和接受的最长时间。
假定网络不可靠,那么第四次发送的ACK
可能丢失,即服务端无法收到这个ACK
,如果服务端收不到这个确认ACK
,服务端会定时向客户端端重复发送FIN
,直到服务单端收到客户端的确认ACK
。所以这个2MSL就是用来处理这个可能丢失的ACK
的。
问题二:为什么握手只要三次,挥手却需要四次?
在TCP连接中,服务端SYN和ACK向客户端发送是一次性发送的,而在断开连接的过程中,服务端向客户端发送的ACK和FIN是分两次发送的。因为在服务端接受到客户端的FIN后,服务端还有数据要传输的话,所以先发送ACK,等服务端处理完自己的事情后就可以发送FIN断开连接了。
下篇预告:
TCP长连接与短连接,与Socket的联系