赞
踩
目录
TCP:
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。
UDP:
Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。RFC 768 描述了 UDP。
为什么要进行三次握手?
当进行第一次握手,网络不好可能会堵塞,所以连接的请求并没有到达服务器端;
但是tcp连接有超时重传的机制,所以再一次发送请求,这时候服务器端接收到了你的请求,他也会返回一个请求给你,这是第二次握手;
但是这时候网络环境突然又好了起来,那个堵塞的请求到达了服务器端,服务器端又给你回了一个请求,但是你又不想给服务器发送请求,这时候服务器的资源会进行占用等待你的请求,为了不使服务器的资源继续占用,你又必须发送一个请求给服务器;
所以要进行3次握手
为什么不能两次握手能进行连接?
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
为啥只有三次握手才能确认双方的接受与发送能力是否正常,而两次却不可以:
第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
刚开始客户端处于 closed 的状态,服务端处于 listen 状态。然后
1、第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_Send 状态。
2、第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。
3、第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。
4、服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。
Ps:(1)SYN=1 表示该报文不携带数据,但消耗一个序号 seq=x,seq=x是客户端的初始化序列号,因为tcp是面向字节流的
(2)SYN=1 表示该报文不携带数据,但消耗一个序号 seq=y,seq=y是服务器的初始化序列号,ACK=1是一个确认号
ack=x+1,表示服务器下次接收到的序号希望是x+1。然后服务器进入到SYN-RCVD等待的状态
(3)ACK=1是一个确认号,seq=x+1是上一次服务器回应的序号要求,ack=y+1表示客户下一次接收到的序号希望是y+1
三次握手的作用:
(ISN)是固定的吗?
三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。 如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
什么是半连接队列?
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题: 服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s, 2s, 4s, 8s, …
三次握手过程中可以携带数据吗?
可以携带数据的。也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。
而对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据页没啥毛病。
四次挥手:
这里特别需要主要的就是TIME_WAIT这个状态了,这个是面试的高频考点,就是要理解,为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭。这其中的原因就是,要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。
至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功就是 ACK 报文,此时处于 CLOSED 状态。
为什么建立连接需要三次,释放连接需要四次?
TCP建立连接要进行3次握手,而断开连接要进行4次,这是由于TCP的半关闭造成的,因为TCP连接是全双工的(即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭,这个单方向的关闭就叫半关闭.关闭的方法是一方完成它的数据传输后,就发送一个FIN来向另一方通告将要终止这个方向的连接.当一端收到一个FIN,它必须通知应用层TCP连接已终止了这个方向的数据传送,发送FIN通常是应用层进行关闭的结果。
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
开启TCP时间戳
在Linux系统中,通过开启TCP时间戳功能,可以避免TIME_WAIT状态的累积。可以通过修改/etc/sysctl.conf
文件,并添加如下配置来开启TCP时间戳:
net.ipv4.tcp_timestamps = 1
然后执行sysctl -p
命令使配置生效。
开启TCP连接复用
TCP连接复用指的是在TIME_WAIT状态的连接被关闭后,将其端口立即重新分配给新的连接,而不必等待TIME_WAIT状态结束。这样可以有效减少TIME_WAIT状态的数量。可以通过修改/etc/sysctl.conf文件,并添加如下配置来开启TCP连接复用:
net.ipv4.tcp_tw_reuse = 1
然后执行sysctl -p命令使配置生效。
调整本地端口范围
本地端口范围指的是用于分配本地端口的范围。如果本地端口范围设置得太小,可能会导致端口耗尽和TIME_WAIT状态的增加。可以通过修改/etc/sysctl.conf文件,并添加如下配置来扩大本地端口范围:
net.ipv4.ip_local_port_range = 1024 65535
然后执行sysctl -p命令使配置生效。
需要注意的是,以上方法仅适用于解决普通的TIME_WAIT问题。在某些特殊情况下,开启net.ipv4.tcp_tw_recycle选项可能会导致问题。因此,不建议开启net.ipv4.tcp_tw_recycle选项。
综上所述,通过开启TCP时间戳、开启TCP连接复用以及调整本地端口范围,可以有效解决大量TIME_WAIT问题,提升系统的性能和资源利用率。在进行相关配置时,需谨慎对待net.ipv4.tcp_tw_recycle选项,以避免潜在的问题。
校验和:在数据传输的过程中,将发送的数据段都当做一个16位的整数。将这些整数加起来。并且前面的进位不能丢弃,补在后面继续相加,最后取反,得到校验和。发送方:在发送数据之前计算检验和,并进行校验和的填充。接收方:收到数据后,对数据以同样的方式进行计算,求出校验和,与发送方的进行比对。如果接收方比对校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。
序列号:确认序号和序号 序列号:TCP传输时将每个字节的数据都进行了编号,这就是序列号。
确认应答:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行ACK确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了多少数据,下一次的数据从哪个序列号开始发。
超时重传:发送方在发送完数据后等待一个时间,时间到达没有接收到ACK报文,那么对刚才发送的数据进行重新发送.这个等待时间是动态计算的,一般500ms为一个单位进行控制。重发一次后,仍未响应,那么等待2*500ms的时间后,再次重传。等待4*500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。
连接管理:三次握手,四次挥手
流量控制:如果发送端的发送速度太快,导致接收端的结束缓冲区很快的填充满了。此时如果发送端仍旧发送数据,那么接下来发送的数据都会丢包,继而导致丢包的一系列连锁反应.TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。
滑动窗口:实际上是接收端接收数据的缓冲区的还剩余多少。接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。而发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小反馈发送端
拥塞控制:TCP引入了慢启动的机制,在开始发送数据时,先发送少量的数据探路。探清当前的网络状态如何,再决定多大的速度进行传输。这时候就引入一个叫做拥塞窗口的概念
延迟应答:如果接收数据的主机⽴刻返回ACK应答, 这时候返回的窗⼝可能⽐较⼩。可以在稍微等一会儿再进行应答(规定等待时间之内),因为处理端口对缓存区的处理速度可能很快, 10ms之内就把刚收到的数据从缓冲区消费掉了;这样的延迟应答返回的窗口可能会更大
短连接的操作过程:建立连接 ——> 传输数据 ——> 关闭连接。
长连接的操作步骤:建立连接 ——> 传输数据 ——> ... (保持连接) ... ——> 传输数据 ——> 关闭连接。
长连接和短连接的优缺点:
短连接的优缺点:
长连接的优缺点:
长连接/短连接的应用场景:
长连接一般多用于需要频繁进行读写操作,点对点通讯,而且连接数不太多的情况。
例如:数据库的连接通常使用长连接,如果用短连接的话,频繁的TCP socket创建和关闭,会造成socket错误,也是对资源的一种浪费。
短连接一般用于不需要频繁进行读写操作,并且连接数很大的情况下。
例如:web网站的http服务一般都用短连接。因为长连接对于服务器来说是要耗费一定的系统资源的,像web网站服务,通常会有大量的客户端连接请求,并发连接量大,使用短连接会更节省系统资源,能够及时响应客户请求。
对于TCP长连接,当通信双方在没有数据传输的时候,如何保持TCP连接一直处于“保活(KeepAlive)”状态,这是一个必须要解决的问题。
在Linux系统中,我们可以使用 netstat、lsof等命令可以查看TCP连接是否处于“ESTABLISHED”状态。
TCP保活的必要性:
导致TCP非正常断连的可能原因:
(1)网络故障
(2)客户端/服务端一侧突然断电或者进程崩溃
保活机制的方式:
应用层的心跳机制:
在应用层中使用心跳(heartbeat)机制来主动检测。具体做法:当TCP连接建立成功后,客户端开启一个定时任务,定时对已经建立连接的对端发送一个心跳请求消息,服务器收到该心跳消息后,返回一个心跳应答消息。如果在超时时间内没有收到服务器的应答消息,则重发心跳请求消息,如果客户端持续多次没有响应,客户端则可以认为该TCP连接不可用,主动断开连接。当然,也可以是服务器端主动发送心跳请求消息给客户端。
TCP协议自带的保活机制:
Linux内核自带的保活机制keep-alive。使用的时候只需要打开keep-alive功能即可。
TCP Keepalive 和 应用层 heartbeat 优缺点:
1、TCP协议的 Keepalive 机制
优点:TCP协议的Keepalive机制由系统内核实现,上层应用程序只需要处理数据的收发,连接异常通知即可,这就减少了应用层代码的复杂度,内核层面的计时器相比应用层,更为高效。
缺点:第一,TCP keepalive机制,位于传输层,由操作系统负责,只能检测到连接是否存活,但不能检测检测连接是否可用。例如,服务器因为某种原因导致负载超高,CPU使用率达到了100%,无法继续响应任何业务请求,但是TCP探针却仍能确定连接状态,这就是典型的连接活着但是服务已死的状态。对于客户端而言,这时最好的选择就是断开连接重新连接到其他服务器上,而不是一直认为当前服务器仍处于可用状态,一直向当前服务器发送那些必然会失败的请求。
第二,TCP keepalive机制 对于连接异常断开的情况不能及时有效地监测到。如果TCP连接的某一方突然异常断开连接,这个时候发送方并不知道对端已经掉线。而此时,如果有数据发送失败,tcp会自动进行超时重传,而重传报文段的优先级是要高于keepalive的探测报文段的,导致探测报文段总是不能发送出去,直到经过较长时间的重传之后,我们才会知道。
2、应用层的HeartBeat 机制
优点:第一,具有更好的灵活可控性。可以控制心跳的监测时机、间隔和流程,甚至可以在心跳包上附带额外信息,最重要的是不光可以检测连接是否存在,还可以检测到连接是否可用,而TCP的keepalive机制只能提供简单的检活功能。
第二,具有通用性。应用层的心跳不依赖传输层协议,如果有一天不用TCP要改用UDP了,传输层不提供心跳机制了,但是你应用层的心跳机制依然可以使用,只需做少许改动就可以继续使用。
缺点:第一,需要开发人员自己实现,增加软件开发的工作量,由于应用特定的网络框架,还可能增加代码结构的复杂度。
第二,应用层心跳的流量消耗会更大,毕竟这本质上还是一个普通的数据包。
在SYN泛洪攻击中,攻击者向目标服务器上的每个端口重复发送SYN数据包,通常使用伪造的IP地址。服务器不知道受到攻击,收到多个看似合法的请求以建立通信。它使用来自每个开放端口的SYN-ACK数据包响应每次尝试。
恶意客户端要么不发送预期的ACK,要么(如果IP地址被欺骗)从一开始就不会收到SYN-ACK。无论哪种方式,受到攻击的服务器都会等待一段时间来等待对其SYN-ACK数据包的确认。
在此期间,服务器无法通过发送RST数据包关闭连接,连接保持打开状态。在连接超时之前,另一个SYN数据包将到达。这导致越来越多的连接处于半开状态——事实上,SYN泛洪攻击也被称为“半开”攻击。最终,当服务器的连接溢出表填满时,对合法客户端的服务将被拒绝,服务器甚至可能出现故障或崩溃。
SYN缓解方法
微块——管理员可以在服务器内存中为每个传入的SYN请求分配一个微记录(少至16字节),而不是一个完整的连接对象。
SYN cookies——使用加密哈希,服务器发送其SYN-ACK响应,其中包含一个序列号(seqno),该序列号由客户端IP地址、端口号和可能的其他唯一标识信息构成。当客户端响应时,此哈希包含在ACK数据包中。服务器验证ACK,然后才为连接分配内存。
RST cookies——对于给定客户端的第一个请求,服务器故意发送无效的SYN-ACK。这应该会导致客户端生成一个RST数据包,告诉服务器出了什么问题。如果收到,服务器知道请求是合法的,记录客户端,并接受来自它的后续传入连接。
堆栈调整——管理员可以调整TCP堆栈以减轻SYN泛洪的影响。这可能涉及减少超时,直到堆栈释放分配给连接的内存,或有选择地丢弃传入连接。
拥塞现象:当网络的吞吐量达到一定的限制后,会发生拥塞现象,那么此时发送的数据包会因为延迟无法到达接收端,因为超时重传机制所以发送端会重新发送数据包,那么网络带宽中本来就很拥塞,那么的话再发数据包就会更加拥塞,由此会形成恶性循环,如果不加控制的话,那么最终网络的有效吞吐量将接近为0.
拥塞控制代价: 需要获得网络内部流量分布的信息。在实施拥塞控制之前,还需要在结点之间交换信息和各种命令,以便选择控制的策略和实施控制。这样就产生了额外的开销。拥塞控制还需要将一些资源分配给各个用户单独使用,使得网络资源不能更好地实现共享。
拥塞控制的四种算法:慢开始、拥塞避免、快重传和快恢复。
TCP是全双工通信,因此每一方的滑动窗口都包括了接收窗口+发送窗口,接收窗口负责处理自己接收到的数据,发送窗口负责处理自己要发送出去的数据。滑动窗口的本质其实就是维护几个变量,通过这些变量将TCP处理的数据分为几类,同时在发送出一个报文、接收一个报文对这些变量做一定的处理维护。
粘包:多个数据包被连续存储于连续的缓存中,在对数据包进行读取时由于无法确定发生方的发送边界,而采用某一估测值大小来进行数据读出,若双方的size不一致时就会使指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
比如说:发送方发送了两个数据,接收方一次收了一个半数据(接收方可能不清楚一个包有多大)
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。
发送方引起的粘包是由TCP协议本身造成的:
接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
在代码中常见体现:
1.UDP 使用场景:
因此UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务,随时都可以发送数据,处理简单且高效。
所以主要使用在以下场景:
主要是一切追求速度的场景上
2.TCP 使用场景:
TCP 使用场景:相对于 UDP,TCP 实现了数据传输过程中的各种控制,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。在对可靠性要求较高的情况下,可以使用 TCP,即不考虑 UDP 的时候,都可以选择 TCP。
特别是需要可靠连接,比如付费、加密数据等等方向都需要依靠TCP
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。