一、TCP/IP概念模型与OSI参考模型对比
OSI参考模型只是一个理想的概念,分为7层,从最底层的物理层、链路层、网络层、传输层、会话层、表示层、应用层,但是TCP/IP的概念模型一般分为四层,即链路层、网络层、传输层、应用层。
OSI中的物理层和链路层被归为链路层,网络层和传输层不变,最后三层统称应用层。
在具体实现中,一般都是TCP/IP模型,分成四层:应用层、传输层、网络层、链路层
1.TCP/IP协议并不是必须从应用层->传输层->网络层的。
这里的ping命令其实就没有经过传输层,而是直接从应用层到了网络层
2.TCP和UDP的区别
- 1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
- 2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。 - 3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
- 4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- 5、TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP连接通过三次握手确保连接的可靠性。
3.TCP特性
- 面向连接的
- 可靠性
- RTT(往返时延,Round-Trip Time)和RTO(重传超时,Retransmission TimeOut)
- 数据排序
- 流量控制
- 全双工
流量控制:滑动窗口,其实很简单,就是每次tcp传输过程中,server和client会携带一个滑动窗口大小,意味着此时它能一次接受多少数据,因为数据在客户端和服务器之间是有缓存的,滑动窗口也就相当于对方告诉自己目前缓冲区的大小,不需要每次发一个数据包就回一个ACK,通过滑动窗口来进行简单的拥塞控制。 - 发送方和接收方都会维护一个数据帧的序列,这个序列被称为窗口
- 发送方的窗口大小由接收方确认
比如第一次客户端向服务端发送了1000个数据,但是服务端处理不过来,则在确认应答的时候,告诉客户端不需要一次发送那么多数据,比如发送500就可以了。这也可以调整大,比如变成3000
使用滑动窗口的目的:
(1)确保数据不丢失
如果发送的数据丢失了可以重新发送
(2)控制发送速度
控制发送速度,以免接收方的缓存不够大导致溢出,同时控制流量也可以避免网络拥塞
4.TCP/IP四层模型,网络层和传输层有什么区别?网络层的主要工作是什么?
网络层负责ip数据报的产生以及ip数据包在逻辑网络上的路由转发。
网络层只是根据网络地址将源结点发出的数据包传送到目的结点(点到点),其主要任务是:通过路由选择算法,为报文或分组通过通信子网选择最适当的路径。该层控制数据链路层与传输层之间的信息转发,建立、维持和终止网络的连接。具体地说,数据链路层的数据在这一层被转换为数据包,然后通过路径选择、分段组合、顺序、进/出路由等控制,将信息从一个网络设备传送到另一个网络设备。
传输层提供端到端通信服务层次,提供可靠及非可靠连接,TCP和UDP。
传输层则负责将数据可靠地传送到相应的端口(端到端),传输层提供了主机应用程序进程之间的端到端的服务。传输层利用网络层提供的服务,并通过传输层地址提供给高层用户传输数据的通信端口,使高层用户看到的只是在两个传输实体间的一条端到端的、可由用户控制和设定的、可靠的数据通路。
二、TCP连接的三次握手
第一次握手
建立连接。客户端发送连接请求报文段,将SYN位置为1,Seq序列号(Sequence Number)设置为x(系统随机生成的);然后,客户端进入SYN_SEND状态,等待服务器的确认;
第一次握手,就是客户端向服务器发送一个请求连接的报文,等待服务器确认,客户端进入SYN_SEND状态。
第二次握手
服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Ack确认序列号(Acknowledgment Number)为x+1(Sequence Number+1),这个是Ack确认序列号,确认序列号都是为收到的报文的序列号+1得到的;同时,自己自己还要发送SYN请求信息,将SYN标志位置为1,并且将ACK标志位也置为1,Seq序列号(Sequence Number)为y(Seq为y,Seq系统随机生成的);服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;客户端进入ESTABLISHED状态(established)。这是服务端请求与客户端建立连接
第二次握手,就是服务器收到客户端的报文段,对报文确认,然后服务器向客户端发送一个SYN+ACK报文请求信息,服务器进入SYN_RECV状态。这是服务端请求与客户端建立连接
第三次握手
客户端收到服务器的SYN+ACK报文段,检查服务端发送过来的报文段ACK标志位是否为1,ack确认序列号是否为x+1。如果正确,然后将Ack确认序列号(Acknowledgment Number)设置为y+1,这个y是客户端收到服务器端发送过来的报文的序列号y,将ACK标志位置为1,向服务器发送ACK报文段。这个报文段发送完毕以后,服务端接收到报文,检查ACK标志位是否为1,且ack确认序列号是否为y+1,如果正确则连接建立成功,此时客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。第三次握手时,客户端发送给服务端的报文的的SYN标志位为0
第三次握手,是客户端回应服务器的ACK报文信息,向服务器发送一个确认。
为什么要进行三次握手
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。其实就是为了解决可靠性。三次握手发送的报文段都会有一个序号,三次握手就是为了交换TCP的初始序号,即客户端发一个Seq=X,服务端就发送确认信号Ack为X+1。客户端发送的连接,并不是先发的就先到,所以要避免混乱就需要通过序号和确认序号。三次握手,其实就是客户端和服务端都需要将自己的初始序列号告诉对方,并且收到对方的确认信息。
TCP是面向连接的,所以需要双方都确认连接的建立。如果是两次连接,那么客户端与服务端建立了连接,但是服务端向客户端进行连接的时候,服务端就无法收到客户端的确认信息,这样服务端与客户端的连接就没有建立。
这里的初始序列号,那么下次进行通信的时候,就是从该初始序列号加上当前要发送的数据的长度,比如客户端的初始序列号seq=500,要发送500bytes字节数据,那么下一次客户端要发送的数据的序列号就是从511开始
三次握手的过程就是通信双方互相告知初始序列号的值,并且确认对方已经收到了初始序列号值的一个步骤。
如果只进行两次握手,至多只有连接发起方的初始序列号能被确认,而服务端的初始序列号并不能被客户端确认,这样客户端就无法知道服务端的初始序列号,则无法正常接收对应的数据序列。
防止服务器端因为接收了早已失效的连接请求报文从而一直等待客户端请求,从而浪费资源
- “已失效的连接请求报文段”的产生在这样一种情况下:Client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。
- 这是一个早已失效的报文段。但Server收到此失效的连接请求报文段后,就误认为是Client再次发出的一个新的连接请求。
- 于是就向Client发出确认报文段,同意建立连接。
- 假设不采用“三次握手”:只要Server发出确认,新的连接就建立了。
- 由于现在Client并没有发出建立连接的请求,因此不会向Server发送数据。
- 但Server却以为新的运输连接已经建立,并一直等待Client发来数据。>- 这样,Server的资源就白白浪费掉了。
如果不采用三次握手,那么服务端接收一个早已经失效的报文并且确认,此时就会认为连接已经确认,但是因为客户端这个时候并没有发送一个有效的连接报文,而是服务端接收了一个失效的连接报文,这样就可能客户端认为没连接,而服务端认为客户端已经连接,造成服务端在等待客户端发来的数据,导致服务端的资源浪费。
因为tcp是全双工,为保证传输的可靠性,需要给每次传输的数据段添加序号,那么初始的序列号就是tcp三次握手真正的意义所在,而为了确保交换双方的初始序号,最少需要三次才行
全双工、半双工、单工的解释
全双工:客户端能给服务端发数据,服务端同时也可以给客户端发数据
半双工:客户端给服务端发数据的时候,服务端只能接收;服务端接收完成之后,可以给客户端发,但是这个时候客户端就不能给服务端发
单工:只能客户端给服务端发
TCP连接是全双工的。
三次握手的漏洞
SYN洪泛攻击:通过网络服务所在的端口发送大量伪造原地址的攻击报文,发送到服务端,造成服务端上的半开连接队列被占满,从而阻止其他用户进行访问。
原理:攻击者客户端利用伪造的IP地址向服务端发出请求(第一次握手),而服务的响应(第二次握手)的报文将永远发送不到真实的客户端,服务端在等待客户端的第三次握手(永远都不会有),服务端在等待这种半开的连接过程中消耗了资源,如果有成千上万的这种连接,主机资源将耗尽,从而达到攻击的目的。
解决的方案:
- 无效连接监控释放
- 延缓TCB分配方法
- 防火墙(最好的方案)
SYN洪泛攻击,一般服务端向客户端发起应答请求的时候,是需要客户端的ip,而洪泛攻击就是伪造这个ip,往服务端发送第一次握手的数据,由于这第一次握手的客户端ip是伪造的,那么服务端往客户端进行第二次握手的时候由于客户端ip是伪造的,则第二次握手就没有结果,所以容易让服务器被拖累,造成死机等问题。
什么时候可以给服务端发送数据?
其实当客户端收到服务端的确认报文之后和请求建立连接的握手之后,向服务端发送第三次连接的时候,就可以带有数据发送给服务端。因为三次握手只需要交换完了初始序列号之后就可以发送数据了。
最早可以给服务端发送数据,必须是在第三次握手的时候,由客户端向服务端发送服务端连接的确认信息。
但是如果TCP三次握手连接成功之后,双方一直不发送数据,那么在一定时间之后,TCP连接就会断开,那么下次就需要重新进行连接
总结
从三次握手,可以总结看出,每次确认的确认序列号都是为收到的报文的序列号+1,SYN是在第一次和第二次的时候为1,ACK是在第二次和第三次的时候为1。
第一次SYN为1是为了让服务器端确认,第二次为1是因为这个值需要客户端来修改,ACK第二次为1是为了让客户端确认,第三次为1,是为了让服务器端确认来进行连接。
Socket的三次握手
这里要注意Socket的connect()和accept()函数分别在三次握手的第几次后返回?
connect在第二次握手,accept在第三次握手。
第一次握手时,创建连接,connect处于阻塞状态,等待服务器accept,第二次握手成功,connect从阻塞返回,accept同样的需要收到客户端ack以后才从阻塞返回,连接建立完成。在accept接收到客户端发送过来的第一次握手报文的时候,此时accept是处于阻塞状态,并且返回一个确认信息和请求连接信息给客户端,客户端connect接收到后,返回信息给服务端,accept等到接收到客户端返回过来的这个报文信息后,就会返回。
这个过程,其实可以认为是客户端在第一个握手的时候调用connect视图连接服务端,这个时候客户端因为connect处于阻塞状态,等待服务端的accept返回,第二次握手的时候,服务端返回信息,客户端接收到之后,connect就会从阻塞状态返回,然后向服务端发送ack确认请求进行第三次握手,当服务端收到确认信息之后,accept就会从阻塞状态返回。
accept是在发送第二次握手报文的时候,进入阻塞状态。connect是客户端在发送第一次握手报文的时候进入阻塞状态。
三、TCP连接的四次挥手
第一次挥手
主机1(可以使客户端,也可以是服务器端),设置Sequence Number,向主机2发送一个FIN报文段(其实就是标志位FIN=1),此时也会发送一个seq序列号,该序列号的值是随机的,比如是x;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;发送FIN包
第二次挥手
主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,ACK标志位为1,Acknowledgment Number为Sequence Number加1,即确认序列号ack=x+1;主机1收到主机2的应答报文后,进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;此时主机2进入CLOSE_WAIT状态,此时与后面的CLOSED状态还是有区别,服务端此时还没有真的关闭。服务端进入CLOSE_WAIT状态,表示当前自己是半关闭状态,即对端已经没有数据发送给自身,自身不需要接收数据了,但是自身与对端之间的连接还并没有关闭,因为此时服务端还可能有数据发送给客户端,还与客户端保持连接。需要服务端向客户端发起第三次挥手,告诉客户端需要关闭服务端与客户端之间的连接,然后客户端确认之后,服务端才会进入CLOSED状态
第三次挥手
主机2向主机1发送FIN报文段(即标志位FIN=1),并且发送一个序列号seq,序列号的值是随机的,比如seq=y,并且还会发送一个确认序列号,该确认序列号其实是与第二次分手时的确认序列号一直,即ack=x+1,请求关闭连接,同时主机2进入LAST_ACK状态;发送FIN包,服务端进入LAST_ACK状态,是要等待客户端的确认包
第四次挥手
主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段(标志位ACK=1),并且发送序列号,该序列号其实就是与第三次分手时的确认序列号一直,即seq=x+1,并且发送一个确认序列号,该确认序列号为第三次分手时的序列号+1,即ack=y+1,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接,进入CLOSED状态;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了,进入CLOSED状态。
比如:当主机1发送关闭请求,并且主机2确认了该关闭请求后,主机1不能发送数据,但是可以继续接收数据。只有当主机2发送了关闭请求,并且主机1确认关闭进入CLOSED状态后,主机1才不能接收数据。
1.为什么要四次分手
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
全双工:其实就是客户端可以向服务端发送,服务端也可以同时向客户端发送,是有两条连接。
挥手要四次:主要是客户端和服务端都需要分别关闭请求,而关闭请求发送出去都需要接收到对方的同意,这样两个关闭请求,两个同意就四次挥手。
四次挥手有可能是服务端发起的,也有可能是客户端发起的。
四次挥手是有可能变成三次的。第二次跟第三次可以合并为一个一次性发送给客户端。但是这样的情况比较小。在主机2发送确认报文给主机1的时候,当主机2没有额外的报文发送给主机1了,那么第二次和第三次分手有可能合并。
而FIN报文也是有可能与数据一起发送,比如主机1发送一个数据给主机2,此时该数据是最后一次发送,那么也有可能同时发送FIN报文。
但是第一次分手也有可能随着最后一次数据一起发送
2.四次挥手的状态转移:
客户端向服务端发送第一次挥手:客户端从established->FIN_WAIT_1状态
服务端向客户端发送第二次挥手:客户端从FIN_WAIT_1->FIN_WAIT_2状态
服务端向客户端发送第三次挥手:服务端从established->LAST_ACK状态,
客户端向服务端发送第四次挥手:客户端从FIN_WAIT_2->TIME_WAIT状态,此时客户端必须在此停留超过2MSL时间之后,客户端就默认断开。一来一回最大的时间
为什么是要两个MSL时间?
- 保证TCP协议的全双工连接能够可靠关闭
- 保证这次连接的重复数据段从网络中消失
解释一:
如果在两个MSL时间内,比如第四次挥手丢了,那么对方就需要在超时后重发第三次挥手的FIN包,客户端接收到服务端的重发的FIN包之后,可以再发一个ACK确认包。如果服务端没收到客户端最后发过来的这个ACK确认包,那么这一来一回,即客户端接收一个重发的FIN包,又重发一个ACK确认包给服务端,这样的一来一回,就是两个MSL时间。
客户端在两个MSL时间内,是不能使用的,需要等到2MSL时间结束之后才可以继续使用。
解释二:
这里规定,在一端处于TIME_WAIT状态的时候,其端口是不允许建立连接的。
如果Client直接CLOSED,而不在TIME_WAIT等待2*MSL时间,此时又有一个客户端又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。即前一个连接的延迟数据包,要到达服务端,然后服务端要响应该延迟数据,这样的一来一回才会最终消失数据段。
MSL:Max Seqment Lifetime,报文最大生存时间(RFC定义为2分钟,大部分操作系统只有30秒)
TIME_WAIT时间,一般是1~4分钟。
四次分手发起者
其实分手的主动发起者并不一定是客户端,客户端和服务端都是可以主动发起分手。