文章目录
- 再谈端口号
- 协议号:
- 端口号范围划分
- pidof(),命令行输入,根据进程名直接拿到进程id
- netstat -nltp :查看网络状态
- UDP协议
- 如何做到向上交互?(分用问题)
- 如何做到封装和解包?
- Linux内核当中,如何看待udp报文(报头)
- UDP特点:
- UDP缓冲区
- TCP协议
- 报头部分
- 序号问题
- 16位窗口大小
- 16位目标端口号:
- tcp的三次握手
- 六个标记位:
- 超时重传的机制
- 连接管理
- 连接状态的变化
- 1. 为什么是三次握手?
- 2. 四次挥手
- 实验验证:主动断开的一方要先进入TIME_WAIT状态
- 理解TIME_WAIT状态
- bind error!啥意思来着?
- 实验验证CLOSE_WAIT
- 滑动窗口
- 快重传vs超时重传
- 流量控制
- 拥塞控制
- 拥塞窗口
- 延时应答
- 捎带应答
- 再次理解三次握手
- 面向字节流
- 数据粘包问题
- TCP异常情况
- 基于TCP的应用层协议
- UDPvsTCP
- 用UDP实现可靠传输(面试题)
- listen的第二个参数
再谈端口号
端口号标识了一个主机上的进程。https默认端口是443,xshell的ssh登录默认是22,http协议的端口号就是80,所以套接字通信的本质就是进程间通信。
协议号:
就是端口号,你是几号协议就有确定的端口号进行绑定的,http协议的端口号就是80
同时向服务器发送请求,要么是主机IP号不同,要么就是一台主机端口号不同,所以服务器都可以区分的开。
端口号范围划分
0-1023:知名端口号,HTTP,SSH这些广为使用的协议端口号都是固定的。
1024-65535:OS动态分配的端口号。客户端程序的端口号就是由OS从这个范围分配的。
- 一个进程是否可绑定多个端口号?1
- 一个端口号是否可以绑定多个进程?0
端口号标识进程,一个进程可以绑定多个端口号。
pidof(),命令行输入,根据进程名直接拿到进程id
netstat -nltp :查看网络状态
- n:能显示成数字的都显示成数字,number
- l:listen状态,监听状态的
- t:tcp
- u:udp
- p: process
UDP协议
传输层协议
如何做到向上交互?(分用问题)
有16位的目的端口号,可直接上传给应用层,找到目标进程
- 分用:1. 报头和有效载荷分离。2. 根据比低端口号交付有效载荷和上层应用
我们写代码的时候,需要绑定端口号,来绑定目标进程
端口号为什么是16位?因为协议规定!
如何做到封装和解包?
因为有定长是8字节的报头,对他的添加和读取之后的取出就是封装和解包的过程。
Linux内核当中,如何看待udp报文(报头)
结构体中位段的形式进行赋值。添加报头就是形成结构体对象并赋值。
UDP特点:
无连接:知道对端IP和PORT就可以直接传输不需要建立连接。
不可靠:没有确认机制,没有重传数据。
面向数据报:对于报文,上层应用要么不读,读就全读完。发多少,服务器收多少,不能够灵活的控制读写数据的次数和数量。对于报文,原样发送,不拆分,也不合并,一次发100字节 就一次读100字节。
UDP缓冲区
系统调用接口与其说是收发函数,不如说是拷贝函数。read recv write send等系统调用函数参数中都有缓冲区以及缓冲区大小的参数。用户写write的时候,将应用层那些字符串,拷贝到udp,tcp的缓冲区当中。拷贝完成之后,具体怎么发,发多少,都是由OS(传输层)来控制。传输层提供传输数据的策略!!
UDP没有发送缓冲区,有接收缓冲区,不能保证UDP报文的顺序和发送的顺序不一致,缓冲区满了,再来的UDP数据就会被丢弃。
-
UDP全双工
既可以recvfrom 也可以sendto ,可以被同时调用。
DHCP:动态主机配置协议(路由器自动给设备配置IP)
DNS:域名解析协议
TCP协议
报头部分
传输层是定各种各样决策的。
- TCP如何做到封装和解包的?
首先,
-
TCP标准首部长度20字节的,首部长度表示的就是报头长度。
四位首部长度,基本单位是4字节(0000~1111),也就是说,如果首部长度是5,那么就是4*5=20个字节大小的空间。最大长度就是1111=15,15 * 4字节=60字节,就是TCP最大长度。
x*4=20,所以四位首部长度为5,表示就是0101,所以基本都填充0101。
所以传来一份数据,读取前20字节就是报头,剩下的就是有效载荷。
去掉20字节的四位首部长度,得到的就是有效载荷,实现的就是解包的过程。加上就是封装的过程。
向上分用的过程依靠的就是16位目的端口号,查找目标进程。
序号问题
TCP叫做保证可靠性,最核心的机制就是基于序号的确认应答机制,tcp常规可靠性-确认应答。
发送方不能确认别人是否已经接收,在没有接收反馈的情况下无法确认。接收到响应的最大的意义是确认了,上次我给他发送的信息他知道了。
通过应答来保证上一条信息被对方100%收到了。tcp并不是100%可靠的,因为通信时总会遇到最新的一条消息是没有被应答的。只要有一条消息有应答,我们就能确认该消息被对方100%收到了,叫做一端到一端的可靠性。能保证客户端到服务端的可靠性也能保证反向的可靠性。
只要你给了我确认,就说明我给你的数据可靠的收到了,TCP的可靠性体现的是对历史数据的可靠性。
发送的数据,都是报文。批量化发送报文的顺序是12345,server收到的顺序一定是12345?被你收到了,不一定是被你按顺序收到的。server端收到的是乱序的,乱序的数据算是有问题的,可靠性除了保证被对方收到,也要保证按序到达,否则可能业务逻辑发生错误。所以包含了32位序号的概念,所以我们接受的时候按照序号接收报文,来保证按序到达。
- 如何确认对应关系呢?
tcp报头中涵盖一个确认序号,是对历史确认报文的32位序号+1。client收到确认应答tcp报文之后,可以通过确认序号,来辨别是对哪一个报文的确定。比如13是确认信号,13之前的所有报文我已经全部收到了,下次客户端从13号报文开始发送。
无论是数据还是应答,发送的都是一个tcp完整报文,不是一串数字序号。可以不携带数据,但是必须要有完整的tcp报头。
一个报文既有序号,也有确认序号,为什么要分开呢?各自占4个字节。记住,TCP是一个全双工的通信协议,你给我发消息的同时,我也可以给你发消息,是可以同时进行的。一条,可能有应答的作用还有发出数据的作用,一条消息两个作用,既有对你报文的确认,还要给你发送数据双方通信的时候,一个报文既可以携带要发送的数据,也可以携带对历史报文的确认。并行的,全双工的方式通信。至此序号保证了可靠性。
对方接收数据的状态你是不知道的,不能一直的发数据,否则只能被遗弃了。不能是有一种饿是你妈觉得你饿。因为你没考虑接受者的状态,因为没有做流量控制导致丢包的问题。
16位窗口大小
tcp协议内部是自带发送和接收缓冲区的,TCP 内部malloc 两段内存空间,发送缓冲区和接收缓冲区。write和send就是应用层和传输层之间的系统调用接口,与其叫做发送接口,不如理解成为拷贝函数。应用层send并不是把数据发送到网络上,而是把数据拷贝到TCP的发送缓冲区中。server端调用read recvf时,会读取接收缓冲区的内容,读取的时候卡住并不是网络出现问题而可能是缓冲区当中并没有数据。
为什么这么设置呢?
-
缓冲区的存在就是为了避免频繁的访问网卡等外设也就是IO的次数,提高效率。
-
应用层拷贝之后直接返回了,不用考虑怎么发送的问题,提高应用层的相应效率。
-
应用层并不知道网络状况和对方信息,也就是你想发也没有办法发。只有OS中的TCP协议知道网络乃至对方状态明细,所以也就只有TCP协议知道如何发送,什么时候发,出错了怎么办等细节问题。
所以TCP才叫做传输控制协议,也就是这才是他本身的作用。因为缓冲区的存在,可以做到应用层和TCP进行解耦。
-
缓冲区和应用层之间也可理解为内核中的生产者消费者模型。
如果对方来不及接收,对方就只能丢弃,所以我们需要流量控制。server应答的时候,如何返回让他数据传输快点还是慢点的信号呢?可以在应答报文当中在报头里面填上:我自己的接收缓冲区中剩余空间的大小,就是16位窗口大小,让用户得知我现在的接受能力,来动态的调整发送。同样的,服务端给客户端发送数据的时候,服务端也会返回自身接收能力这样的数据。流量控制根据对方缓冲区的接收能力调整发送和接收数据的速率,不只是减慢。
16位目标端口号:
创立套接字就是打开一个文件,是文件就会附带缓冲区,通过哈希的方式找到端口号对应的进程,根据进程中的文件指针找到对应的缓冲区,将数据写入文件缓冲区,再将文件缓冲区的数据发送到目标进程,这样数据就属于那个进程了。
tcp协议是面向链接的,tcp socket要通信的时候是需要先connect的,先建立连接,那么如何建立连接呢?
tcp的三次握手
=>三次数据交换=>交换三次报文,server可能在任何时候都有可能有很多的client发送的报文再向server发送数据,此时可能server在给别人服务。server首先面临的是大量的TCP报文,如何进行区别类别?通过TCP的标志位进行区分,表征的是不同种类的TCP报文,这就是为什么有不同种类的标志位。
六个标记位:
ACK
确认序号+ACK:1,表征自己是对哪一个请求的相应的确认报文,几乎所有的TCP通信的过程中,ACK都会被设置。
SYN
链接建立请求的标记位,就说明这条报文是连接请求报文,当server接收到请求之后,就要完成三次握手。
-
三次握手的过程:发送返回的都是报头文件,将SYN设置为1区分是连接建立报文,过程如下图:
-
理解什么是连接呢?
server存在大量的连接,server得如何管理这些连接呢?先描述再组织。
建立连接的本质:三次握手成功,一定要在双方的OS内,已经为维护该连接创建相应的数据结构struct{},所以双方维护连接是有成本的(时间+空间)。
-
为什么是三次握手?为什么会更担心第三次握手呢?
因为第一次和第二次是有应答的,因为没有应答,会有被丢失的风险,三次握手不是必须成功的, 是一个以较大概率建立连接的过程。
-
三次握手的过程中是伴随着时间的流逝的。
-
RST
client在发送ACK之后,就认为连接建立好了。ACK可能还没有被server收到,一般双方连接成功是存在一段时间差的。那么如果第三次的ACK丢失了呢?server认为自己的第三次握手还没完成,client已经认为连接完成了就开始发送数据,server就可能向client发送响应回的报文,携带RST,client就知道错了。RST就是重置异常连接的。只要是双方连接建立异常,都可以进行reset进行重置。访问服务器是我浏览器出现RST大概率就是连接的问题。
PSH
server接收缓冲区快要打满了,client让server应用层快点接收数据,发送的报文里面PUSH设置为1,尽快接收缓冲区的数据。
URG
因为TCP有按序到达,每一个报文什么时候被上层读到基本使确定的!如果想让一些数据插队呢?可以设置紧急指针标记位URG,标明该报文携带紧急数据需要被优先处理。
那么紧急数据在哪里就由16位紧急指针指向,报文中那个中紧急数据的位置,只能传输一个字节。send()flags参数可以被设置MSG_OOB,带外数据(TCP正常流之外插入的)。
FIN
- 四次挥手
建立连接的一般是client,断开连接是双方随时都有可能的。发出想要断开连接的报文,携带FIN,server返回携带ACK的表示同意了,再发一个表示断开吧携带FIN的报文,client再返回ACK,就完成了四次挥手。断开连接。以四次挥手的方式达成链接关闭的一致认识。断开连接时需要双方的同意的,离婚证都得签字。
-
TCP对于每个字节的数据都进行了编号,即为序列号,是面向字节流的。
发送缓冲区可以认为是一个大数组,数据从应用层拷贝到发送缓冲区数组时就天然的带上来数组下标,也就是编号。当我想发送前1000个字节时,只需要将1000下标的数据发送,前面的自动跟出去,然后响应报文1001就确认之前的数据都接收成功了。
超时重传的机制
这种可靠性机制报头中并没有体现,设置定时器给发出去的每个报文。当你没有收到对方的ACK确认接收到信号,是这个文件真的传丢了吗?可能是对方的应答丢了,进行超时重传。只要是重传的,序号一定是相同的,server端直接实现去重的效果,所以就直接重传就完了。
-
时间设置问题
网络是变化的,网络通信的效率是变化的,发送数据得到ack的时间也是浮动的,超时重传的时间一定是浮动的,Linux中超时以500ms为一个单位进行控制,每次判定超时重传的时间都是500ms的整数倍,2* 500,4* 500指数形式递增。也是有次数限制的,如果因为某种原因,断了重连时,无脑RST,就可以重新建立连接。
ACK丢了的话,我就直接超时重传呗!
-
三次握手是双方操作系统中TCP协议自动完成的,应用层不参与!!
-
client->connect就是在发动三次握手。accept之后,一定是在三次握手之后。
-
在tcp中不要认为用户的发送行为会直接影响tcp的发送逻辑,根本不影响,因为存在缓冲区做解耦操作。
连接管理
连接状态的变化
1. 为什么是三次握手?
过程是确定对方好着没?网络怎么样?tcp是全双工的。
- 确认双方主机是否健康
TCP是全双工的,既可以收也可以发送。只有对方发回来响应,才能确认自己的发送通道是开通的,自己曾经发的消息已经被收到了。
- 验证全双工,三次握手是能看到验证双方都有收发能力的最小的次数。
全双工得到验证,说明主机状态,IO情况和网络状态都是好的。为了验证网络交互状态才有建立连接并不是为了连接而连接。三次就够了。
如果只有一次握手就建立连接,因为服务器对于客户端的报文是有成本进行维护的,客户端一次连接请求就能够建立成功的话成本太低,如果客户端重复的发送消息要求建立连接,服务器就必须对应的设置空间进行维护,太容易受到攻击了。
两次握手也差不多,服务器只发送一次子人就认为已经建立好连接,即便发给client的数据已经丢失。服务器也会自身维护那些所认为的建立好的连接,但是可能没有后续了。服务器端也是容易被人发送海量资源进行攻击的,充满大量的连接,不能很好的预防这种SYN洪水,太占用server空间资源。
三次呢?相对而言被攻击的成本会高一点。
在接收到client的反馈之前,也就是只给我发送大量的SYN且不给我响应ACK,server端并不认为建立成功,也就不会为你维护连接结构体,资源并不会特别被消耗。算是稍微提高了攻击的成本,是在SYN和ACK的历史信息是匹配的对象的基础之上,要做到信息匹配相当于一次正常的三次握手,对服务器有消耗,对client也是有成本的。建立连接之后,服务端是可以拿到IP和端口号信息的,如果连接建立了很多是可以在应用层进行加入黑名单操作的。
上面的保护措施也是有攻击手法进行攻克的。是用来限制君子的,限制不来小人。
2. 四次挥手
断开连接实现了server端资源的释放,所以没有目的是通过FIN攻击的,所以四次挥手更加强调功能性,断开连接的本质双方达成连接都应该断开的共识。四次挥手本质就是通知对方的机制,是协商断开连接的最小次数。
加密的永远是应用层的数据资源,对于IP和PORT 都是暴露的。
发送ACK之后server的状态是close_wait,主动断开的一方,要进入一个TIME_WAIT状态,此时连接有没有被释放呢?发出ACK之后,他认为自己的四次挥手已经完成了,TCP不会让他立即释放资源,等一会,这个时候的状态就是TIME_WAIT状态。
实验验证:主动断开的一方要先进入TIME_WAIT状态
netstat -nltp查看主机连接状态。
将客户端accept拿链接注释,只是bind并且connect,server端显示建立连接成功ESTABLISHED,就说明accept是将已经建立的连接拿上来,我并没有将服务端的套接字accept连接仍然建立成功。
拿起连接之后,首先断开连接的一方先进入TIME_WAIT状态。
理解TIME_WAIT状态
MSL,报文最大传送时间,从一点到另一点,在网络中最大生存时间。TIME_WAIT时间一般是等待2倍的MSL。为什么要等待,为什么是2MSL这么长时间?
-
当最后一次发送ACK时,数据并不会再3增多。尽量保证历史发送的网络数据在网络中消散,避免有的数据没有被双方收取。2倍至少保证了一来一回的时间,最多残留的数据就是2MSL。因为是数据断开阶段不会再进行数据的更新了。
-
无法保证发送的ACK对方收到了,如果我还收到了对面发来的FIN,就说明我发的ACK丢了,如果我没再次收到FIN,我就认为他收到ACK了**,没有消息就是好消息**。
所以就是尽量保证最后一个ACK被对方收到了。
bind error!啥意思来着?
关掉服务之后,再次马上连接就会binderror。因为我是主动连接的一方,进入TIME_WAIT,这个端口依然还在被占用,端口号就是binderror,不能被两个进程占用一个端口号。
-
系统接口
setsockopt();
实现让服务可以立马重启。实现直接复用。但是仍然存在TIME_WAIT,但是并不影响我们直接重新使用。- 无法立即重启会有什么危害?
双11,服务器一旦挂掉之后,服务器就是内个主动断开的连接无法立即重启,损失可大了。就需要设置地址复用这样的函数。端口号是不能随意改变的。
一个close 就是两次挥手,客户端和服务器都调用一次close。
实验验证CLOSE_WAIT
如果客户端先关闭连接,服务器并不处理,只是得到了sock,服务器不调用close,四次回收的后两次就不会执行,服务器就会一直维护着这个连接而客户端已经关掉了,server就处于CLOSE_WAIT状态对服务器资源占用很大,然后再close_wait状态,服务器就不会进入LAST_ACK状态。
启示:
- 如果一个fd已经被用完,千万不要忘记释放。
- fd是有限的云服务器打开文件限制时10万个,fd泄漏。避免服务器无法接收新的连接。
滑动窗口
和16位窗口大小(自身缓冲区剩多少空间)有关吗?有。
你给对方一次填充多少大小的数据呢?并不是由发送方决定,而是由接收方决定。
- 滑动窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。
因为之前我们发送一次报文,收到应答之后再发送下一个报文,为了提高效率一次我们就发很多的报文,将传输时间重叠,然后带着序号保证按序接收。
一次发很多发送前4个段的时候,不需要等待任何ACK,直接发送,但是确认应答还是应该有的,可以晚点来,但是不能不来。
- 滑动窗口是发送缓冲区的一部分。
16位窗口大小表示的是接收缓冲区剩余的空间大小,此时此刻的接收能力。
滑动窗口并不是确定大小的,是根据接收地区的接受能力决定的。如果对面就干收到缓冲区中,上层就是不拿,那么他反馈回来的接受能力就在减小,此时滑动窗口左边一直向右,可以不断减少甚至为0,右侧没有任何变化。如果对面上层将数据读取出来很多时,就可以拓展窗口大小,向右移动的同时,右侧也向右扩大。
- 如果1000-2000的ACK收到了,中间区域3000-4000的ACK并没有收到,后面的区间5000-6000数据的反馈也陆续收到了,
确认序号的含义就是我已经收到了历史数据中的所有数据,也就是即使没有收到中间的区间数据的反馈,也代表完成了数据的接收。
如果中间数据丢了,返回的确认序号一定是到中间3000-4000那个地方的也就是4001,就进入超时重传的板块。
注意:
发送缓冲区可以认为是一个数组,每一个下标对应储存一个字节,所以滑动窗口的本质就是两个左右指针指向的一块区域,已经发送了但是还没有接收到确认信号。
win_start+=确认序号,win_end+=对方通告的窗口大小
另外发送缓冲区是环形的,可以一直转。如果满了,你调用readwrite时就会阻塞住。
少量丢包的情况下,tcp协议是允许有一些丢包的,只要接收到确认序号,就能保证之前的那些包都已经被收到了。
如果中间数据包真的丢失了,后续的也收到了,但是主机B应答时的确认信号一定是丢的那部分开始的那一个,也就是主机A连续三次的接收到相同的确认序号,就说明部分丢包了,就需要进行快重传。没有应答,就没法确认丢包之后的数据有没有被接受。如果大量丢包,就是另外一回事了。
快重传vs超时重传
提高效率的vs兜底的,快重传是有条件的。
流量控制
根据接收端的处理能力来决定发送端的发送速度的这种机制就是流量控制。根据返回的应答中的接受能力调整窗口大小。
-
什么时候知道的对面的接受能力呢?
取决于对方什么时候给我发送的第一个报文。第一个报文是在三次握手的时候我们就已经交互了,绝对不会携带任何数据,握手期间协商窗口大小。SYN报头文件中包含着窗口大小,前两次握手就协商了窗口大小。根据对方的窗口大小来设置自己的滑动窗口的初始值。
-
如果我的接收缓冲区窗口大小为0怎么办?就不发了。那什么时候再发呢?时间一长,主机A可以发送一个含有PSH的报头,那在主机B在接收之后发送应答包含自己读取之后的窗口大小;如果还是为0,或者不回复,那就轮询访问。
拥塞控制
协议考虑中间的网络情况
引例: 发送数据丢了一点,我就重传呗!如果丢了很多,那我还继续重传吗?别传了,说明网络出现了问题,等一会,等网络缓冲好了。贸然重传大量的数据可能雪上加霜。不仅仅是自己在控制,网络一出现问题,大家都会执行拥塞控制。这种机制就是拥塞控制,控制的是网络的问题。
-
已经识别到有拥塞之后,TCP应该怎么应对呢?
慢启动机制,先发少点东西摸摸情况。
拥塞窗口
滑动窗口=min(拥塞窗口,对方的窗口大小);
流量控制,网络拥塞两方面都要进行考虑。拥塞窗口是逐渐增大,浮动的。出现网络拥塞后,根据窗口大小重新调整标准
慢启动的前期是指数型数增长,由探测网络健康到恢复网络健康状况。
为了避免出现网络拥堵所以需要:线性增长阈值,让他到这个阶段之后由指数增长到线性增长,来缓冲。指数级开始,线性级增长。
-
为什么要采用指数级开始呢?慢启动指的是试探阶段慢
指数级增长开始的时候并不是很快,短期内处于能让人接受的范围,对于发送方而言是一步又一步的试探。当试探到对方可以接收依旧是网络状况基本恢复正常的时候就会进入指数爆炸期间,从而快速的恢复之前的传输效率。
网络状况是变化的,发送端始终有指数增长,也就是说拥塞情况是必然的。造成拥塞之后然后再进行调整达成最优。
当TCP开始启动的时候慢启动阈值等于窗口大小,在每次超时重发的时候慢启动的阈值会变成原来的一半同时拥塞窗口设置回1.
当双方都比较稳定的时候就不会造成丢包的问题,然后就可以持续的快速的进行数据传输。
归根结底就是TCP协议想要尽可能的把数据传输给对方,但是又要避免给网络造成太大压力的这种方案。群体主机都需要遵守。
延时应答
接受数据之后,不要急着返回ACK,在不超时的情况下,等待上层尽可能取走数据,然后再返回就可以返回更大的窗口大小。也并不是任何情况都适用。
捎带应答
回复你的问题之后,我还想再携带一些我想给你的数据在回复你的报头之后。大部分情况下是既有ACK设置为1又有其他数据。
再次理解三次握手
实际是四次握手,只不过将SYN和ACK放到一起了,是一种捎带应答。
面向字节流
由于缓冲区的存在TCP的应用程序的读和写次数不需要一一匹配,就叫做字节流。UDP一次发多少对面就得接收多少,而TCP中可能一个报文发了10次,对面可能一次就读完了。
数据怎么来的你并不关心,数据本身是不存在边界的,流。你只要读就有数据,就是TCP按照字节为单位进行发送数据,现在能发所少字节就发多少字节,别的啥也不管,所以叫字节流。比如水龙头接水。
UDP报头有16位UDP长度,可以将报文数据进行区分。
TCP报头没有什么总长度,字节流并没有,只是保证他存在就行,没有区分数据长度,也不知道这个数据代表的是什么,就是单纯字节发送流水一样。
系统眼里都是字节流,什么数据什么配置文件都是用户的角度。至于区分字节角度,都是双方用户应用层协议(http(contentLength)啥属性)进行区分设置的,TCP根本不管什么\n代表说是一行结束的标志,就是当作字节。
应用层从传输层读取数据的次数是随机的,传输次数和接收次数也是不一样的,这就是面向字节流。
数据粘包问题
TCP缓冲区可能有很多报文数据,多读少读的问题,用http中的属性读取数据时,不小心读取别人的数据的问题,就叫做粘包问题,解决粘包问题是应用层根据协议进行解决的,TCP自身没办法解决,因为他认为都是字节,也没办法区分。
用户根据协议解决的伪代码。按照行读取,什么特殊字符区分,字描述字段,定长报文等协议。
协议的各种格式都是为了避免粘包问题,方便用户层读取。
http协议就是添加特殊字符+描述字段
,将各个报文之间进行区分的。
UDP就不存在粘包问题,是面向数据报的,报头文件中包含了16位总长度的,8个字节的报头,剩下的就是有效载荷。有多少读多少。
TCP异常情况
进程中止:进程中止会释放文件描述符,仍然可以发送FIN,和正常关闭没什么区别。文件的生命周期是随进程的,OS断开连接自动四次挥手,是需要一定时间的。
比如,如果连着网线你突然要关机,那么就会先将进程中止,此时进程打开的文件以及电脑的各种网络连接就会检测到连接异常,先进行四次挥手断开连接然后关闭进程进而关机。相比于没有联网的直接关机显然时间要更长。
如果突然断电网络断开,对方是不知道的怎么回事的,所以需要RST对连接进行重置。拔网线,客户端是不会给服务器发送消息,server认为你掉线了,不扣分。但是你退出客户端,就会给服务器发消息判定你是逃跑。打游戏的时候就会扣你信誉分。
TCP自己也内置了一个保活定时器,会定期询问对方是否还在。如果对方不在,也会把连接释放。
基于TCP的应用层协议
HTTP HTTPS SSH Telnet FTP SMTP
UDPvsTCP
TCP用于可靠传输,应用于文件传输,重要状态的更新。更墨迹,你网络不好,为了保证可靠性,就会要求更高的标准,反而会造成不必要的时间的浪费,体验下降。转账什么的得可靠点。
UDP用于告诉传输和实时性较高的通信领域。更直白,你网络好,清晰度就高。视频画质内个自动,就是UDP,因为简单。延迟的原因,上传的文件到网络中,进行一定的缓存,推送给你的慢一点,如果立马推送给你,对机器可能要求更高一点,如果人太多,就会忙不过来了。如果进行了一定的缓存,我就有时间进行缓冲,处理更加从容。
用UDP实现可靠传输(面试题)
找几招TCP的可靠性机制,确认应答,超时重传,引入序列号保证数据的顺序。
listen的第二个参数
不Accept,连接是可以被建立好的。建立好一定的ESTABLISHED之后,其他的就会是SYN_RECV的,并不认为三次握手已经完成了,也就是并不是每一个连接我的都可以连接成功ESTABLISHED。
也就是在TCP层建立正常连接限制了三次握手成功的个数就叫做listent的第二个参数+1
。不是只能在服务器端维护几个链接,可以ACCEPT上去,这个状态就是还没被accept的个数。构成全连接队列(用于保存处于established状态但是应用层还没调用accept取走的请求)长度受第二个参数的影响,满了之后就无法让当前连接的状态进入established状态了。
接收到SYN之后的状态就是SYN_RECV,构成半连接队列(维护SYN_RECV状态的请求),长度是由OS决定的,经过一定的检测之后加入到全连接中。SYN洪水就是半连接队列中挂着,如果半连接队列满了,即使我是一个正常用户,我连半连接都进不去就更无法进入全连接队列了。
-
为什么要维护队列?
比如,吃海底捞在外面排队,给一排凳子让你坐着等,不给凳子谁站着等啊。
全连接队列,短队列,一旦出现空桌就让你坐保证都是满的。
-
为什么这个对队列不能太长?
维护长队列是需要成本的。
-
为什么这个队列不能没有?
为了保证时刻服务器中长连接都是满的,利用率是最高的。
http将文本信息以字节流的方式交给传输层。