运输层
运输层概述
运输层提供应用层端到端通信服务,通俗的讲,两个主机通讯,也就是应用层上的进程之间的通信,也就是转换为进程和进程之间的通信了,我们之前学到网络层,IP协议能将分组准确的发送到目的主机,但是停留在网络层,并不知道要怎么交给我们的主机应用进程,通过前面的学习,我们学习有mac地址,通过mac地址能找到同一个网络下主机,有IP地址,通过ip地址能找到不同网络下的网络,结合mac地址就能找到对应主机,那么怎么找到主机应用进程呢,肯定也有一个东西来标识它,那就是我们常说的端口了。
端口
占有16位,其大小也就有65536个,是从0~65535.也就是一台计算机有65535个端口,主机之间的通讯,也就是应用进程之间的通讯,都要依靠端口,一个进程对应一个端口,进程A和进程B通信,进程A分到的端口为60000,进程B分到的端口为60001,进程A通过端口60000发送数据给进程B,就知道要交给60001端口,也就到了进程B中,这样就达到了通信的目的。
熟知端口、登记端口、客户端端口
- 熟知端口:0-1023, 也就是一些固定的端口号,比如http使用的80端口,意思就是在访问网址时,我们访问服务器的端口就是80,然后服务器那边传网页的数据给我们。
- 登记端口:1024-49151,比如微软开发了一个系统应用,该应用在通讯或使用时,需要使用到xxx端口,那么就要去登记一下这个端口,以免有别人公司的应用使用同一个端口号,例如,windows系统中的3389端口,就是用来实现远程连接的,就固定了这台计算机如果要使用远程连接服务,就打开3389端口,别人就能使用远程连接连你了,默认是不打开的。
- 客户端端口:49152-65535,一般我们使用某个软件,比如QQ,等其他服务,随机拿这个范围内的端口,而不是去拿前面哪些固定的,拿到等通讯结束后,就会释放该端口。
运输层就是将两个端口连起来通信的介质,不然光知道两个端口有什么用,怎么通信的,还是要靠运输层来做这个事情,其中重要的就是靠两个协议,UDP和TCP协议。
UDP协议
UDP:User Datagram Protocol 用户数据报协议,无连接、不可靠
- 无连接:意思就是在通讯之前不需要建立确定连接,直接传输数据。
- 不可靠:是将数据报的分组从一台主机发送到另一台主机,但并不保证数据报能够到达另一端,任何必须的可靠性都由应用程序提供。在 UDP情况下,虽然可以确保发送消息的大小,却不能保证消息一定会达到目的端。没有超时和重传功能,当 UDP 数据封装到 IP 数据报传输时,如果丢失,会发送一个 ICMP差错报文给源主机。即使出现网络阻塞情况,UDP也无法进行流量控制。此外,传输途中即使出现丢包,UDP 也不负责重发,甚至当出现包的到达顺序杂乱也没有纠正的功能。
UDP的首部格式
UDP首部
- 源端口号:占16位,源主机的应用进程所使用的端口号
- 目标端口号:占16位,目标主机的应用进程所使用的端口号,也就是我们需要通信的目标进程
- UDP报长度:UDP用户数据报的长度,数据部分+UDP首部之和为UDP报长度。
- 检验和:检验和是为了提供可靠的 UDP首部和数据而设计,这里不要和上面的不可靠传输搞混淆了,这里提供可靠的UDP首部,是因为一个进程可能接受多个进程过来的报文,那么如何区分他们呢,就是通过5个东西来进行区分的, “源IP地址”、“目的IP地址”、“协议号”、“源端口号”、“目标端口号”的,这个检测可靠,是检测接受哪个正确的报文,也就是说是哪个报文要进这个端口,那个不可靠,说的是这个报文可能丢失,可能其中数据损坏了我们不关心,但是这些的前提是,你得传输到正确的目的地去。
UDP伪首部
就是拿到IP层的一些数据,因为要进行检验和,就必须要有这些数据。其中检验的算法跟IP层中检验首部的办法是一样的。
一个目标进程中,其中的报文,目标端口,目标ip地址肯定都一样的,但是源IP地址和源端口就可能不一样,这就说明了不同源而同一目的地的报文会定位到同一队列。这跟接下来我们要讨论的TCP不一样,因为UDP是无连接的,大家都是用这一条通道,所以其队列中就会出现上面所说的这样的情况。
使用UDP协议的例子:
- 应用层协议中DNS,也就是根据域名解析ip地址的一个协议,他使用的就是UDP
- DHCP,这个是给各电脑分配ip地址的协议,其中用的也是UDP协议
- IGMP,我们说的多播,也就是使用的UDP,在多媒体教师,老师拿笔记本讲课,我们在下面通过各自的电脑看到老师的画面,这就是通过UDP传输数据,所以会出现有的同学卡,有的同学很流畅,就是因为其不可靠传输,但是卡一下,对接下来的观看并没有什么影响
TCP协议
TCP协议是面向连接的、可靠传输、有流量控制,拥塞控制,面向字节流传输等很多优点的协议。其最终功能和UDP一样,在端和端之间进行通信,但是和UDP的区别还是很大的。
TCP报文的结构
- 源端口号
- 目标端口号
- 序列号:因为在TCP是面向字节流的,他会将报文度分成一个个字节,给每个字节进行序号编写,比如一个报文有900个字节组成,那么就会编成1-900个序号,然后分几部分来进行传输,比如第一次传,序列号就是1,传了50个字节,那么第二次传,序列号就为51,所以序列号就是传输的数据的第一个字节相对所有的字节的位置。
- 确认应答:如刚说的例子,第一次传了50个字节给对方,对方也会回应你,其中带有确认应答,就是告诉你下一次要传第51个字节来了,所以这个确认应答就是告诉对方要传第多少个字节了
- 首部长度:就是首部的长度
- 保留:给以后有需要在用,这个保留的位置放的东西是跟控制位类似的
- 控制位:目前有的控制位为6个
- URG:紧急,当URG为1时,表名紧急指针字段有效,标识该报文是一个紧急报文,传送到目标主机后,不用排队,应该让该报文尽量往下排,让其早点让应用程序给接受。
- ACK:确认,当ACK为1时,确认序号才有效。当ACK为0时,确认序号没用
- PSH:推送,当为1时,当遇到此报文时,会减少数据向上交付,本来想应用进程交付数据是要等到一定的缓存大小才发送的,但是遇到它,就不用在等足够多的数据才向上交付,而是让应用进程早点拿到此报文,这个要和紧急分清楚,紧急是插队,但是提交缓存大小的数据不变,这个推送就要排队,但是遇到他的时候,会减少交付的缓存数据,提前交付。
- RST:复位,报文遇到很严重的差错时,比如TCP连接出错等,会将RST置为1,然后释放连接,全部重新来过。
- SYN:同步,在进行连接的时候,也就是三次握手时用得到,下面会具体讲到,配合ACK一起使用
- FIN:终止,在释放连接时,也就是四次挥手时用的。
- 窗口:指发送报文段一方的接受窗口大小,用来控制对方发送的数据量(从确认号开始,允许对方发送的数据量)。也就是后面需要讲的滑动窗口的窗口大小
- 检验和:检验首部和数据这两部分,和UDP一样,需要拿到伪首部中的数据来帮助检测
- 选项:长度可变,介绍一种选项,最大报文段长度,MSS。能够告诉对方TCP,我的缓存能接受报文段的数据字段的最大长度是MSS个字节。如果没有使用选项,那么首部固定是20个字节
- 填充:就是为了让其成为整数个字节
面向连接
(三次握手):在通信之前,会先通过三次握手的机制来确认两端口之间的连接是否可用。而UDP不需要确认是否可用,直接传。
三次握手机制
- 一开始客户端和服务端都市关闭状态,但是在某个时刻,客户端需要和服务端进行通信,此时双方都会各自准备好端口,服务器段的端口会处于监听状态,等待客户端的连接。客户端可会知道自己的端口号,和目的进程的端口号,这样才能发起请求。
- 第一次握手:客户端想与服务器进行连接了,所以状态变为主动打开,同时发送一个连接请求报文给服务器段SYN=1,并且会携带x个字节过去。发送完请求连接报文后,客户端的状态就变为了SYN_SENT,可以说这个状态是等待发送确认(为了发送第三次握手时的确认包)
- 第二次握手:服务端接收到连接请求报文后,从LSTTEN状态变为被动打开状态,然后给客户端返回一个报文。这个报文有两层意思,一是确认报文,而可以达到告诉客户端,我也打开连接了。发完后,变为SYN_RCVD状态(也可以说是等待接受确认状态,接受客户端发过来的确认包)
- 第三次握手:客户端得到服务器端的确认和知道服务器端也已经准备好了连接后,还会发一个确认报文到服务器端,告诉服务器端,我接到了你发送的报文,接下来就让我们两个进行连接了。客户端发送完确认报文后,进入ESTABLISHED,而服务器接到了,也变为ESTABLISHED,进入到ESTABLISHED状态后,连接就已经完成了,可以进行通信了。
问题:为什么需要第三次握手,有前面两次不就已经可以了吗?
假设没有第三次握手,客户端发送一个连接请求报文过去,但是因为网络延迟,在等待了一个超时时间后,客户端就会在重新发一个请求连接报文过去,然后正常的进行,服务器端发回一个确认连接报文,然后就开始通讯,通讯结束后,那个第一次因为网络延迟的请求连接报文到了服务器端,服务器端不知道这个报文已经失效,也发回了一个确认连接报文,客户端接收后,发现自己并没有发送连接请求(因为超时了,所以就认为自己没有发),所以对这个确认连接请求就什么也不做,但是此时客户端不这么认为,他认为i连接已经建立了,就一直打开着等待客户端传数据过来,这就造成了极大的浪费。如果有了第三次握手,那么客户端就可以通知服务器了。所以第三次握手也很重要。
同时打开连接请求
正常情况下,通信一方请求建立连接,另一方响应该请求,但是如果出现,通信双方同时请求建立连接时,则连接建立过程并不是三次握手过程,而且这种情况的连接也只有一条,并不会建立两条连接。同时打开连接时,两边几乎同时发送 SYN,并进入 SYN_SENT状态,当每一端收到 SYN 时,状态变为 SYN_RCVD,同时双方都再发 SYN 和 ACK 作为对收到的 SYN 进行确认应答。当双方都收到 SYN 及相应的 ACK 时,状态变为 ESTABLISHED
-
可靠传输
通过数据编号和积累确认、以字节为单位的滑动窗口、超时重传时间 、快速重传这四个方面来达到可靠传输的目的。- 数据编号:将每个字节进行编号,有900个字节,就从1到900进行编号
- 积累确认:服务器端不是接收到一个字节就发一个确认,那样效率太低,而是当接收到4,5个时,在发送一个确认,那么在之前的确认之前的数据就算发送成功了的。
- 滑动窗口:这个跟在数据链路层讲个滑动窗口一样。每次能发送的数据是在此窗口中的,接到了多少数据,就往后滑多少数据
- 超时重传时间:这个也在链路层讲过,如果等待一段时间后,还没接收到确认报文,那么就重新传
- 快速重传:在滑动窗口中的应用,比如传了12346到服务器端,老办法是在4之后的所有数据度要重新传,而这个快速重传就只需要等待传了5这个序号,就可以继续往下接收数据了。
-
流量控制
在传输层中,有接受缓存和发送缓存这两个东西的存在,所以每次发送数据过去另一端时,都会把这些数据给带过去,让对方知道自己的这两个缓存的大小,然后来合理的设置自己的发送窗口的大小,如果对方的缓存快满了,对方在传送数据过来的时候,就会告诉自己,少发一点数据过来,自己就设置滑动窗口小一点,让对方有缓冲的机会,而不会导致缓存溢出,不让自己的报文被丢弃。 -
拥塞控制
其实跟流量控制差不多,但是站的角度更大,此时既考虑了对方接收不过来,缓存太多溢出导致,又考虑在线路中,线路上的传输速率就那么大,但是有很多人同时用,发送的数据太多,就会使线路发现拥塞,也就是路由器可能转发不过来,导致大量数据丢失,这两个问题。所以拥塞控制这个解决方案,大概意思就是当检测到有网络拥塞时,就会让自己的滑动窗口变小,但具体是怎么变化的,就是根据算法来算了,发送窗口的上限值 = Min[rwnd,cwnd]- rwnd:接受窗口,根据接受缓存,而定的接受窗口,接收缓存还有很多,那么接收窗口就大
- cwnd:拥塞窗口,根据线路中的拥塞状况来决定,线路中不拥塞,那么此窗口就大,
发送窗口是取两个中较小值。这个还是可以理解的。慢启动算法、快速恢复算法、结合来达到对拥塞进行控制的
TCP释放连接时的四次挥手
- 第一次挥手:从ESTABLISHED变为主动关闭状态,客户端主动发送释放连接请求给服务器端,FIN=1。发送完之后就变为FIN_WAIT_1状态,这个状态可以说是等待确认状态。
- 第二次挥手:服务器接收到客户端发来的释放连接请求后,状态变为CLOSE_WAIT,然后发送确认报文给客户端,告诉他我接收到了你的请求。为什么变为CLOSE_WAIT,原因是是客户端发送的释放连接请求,可能自己这端还有数据没有发送完呢,所以这个时候整个TCP连接的状态就变为了半关闭状态。服务器端还能发送数据,并且客户端也能接收数据,但是客户端不能在发送数据了,只能够发送确认报文。客户端接到服务器的确认报文后,就进入了FIN_WAIT_2
状态。也可以说这是等待服务器释放连接状态。 - 第三次挥手:服务器端所有的数据度发送完了,认为可以关闭连接了,状态变为被动关闭,所以向客户端发送释放连接报文,发完之后自己变为LAST_WAIT状态,也就是等待客户端确认状态
- 第四次挥手:客户端接到释放连接报文后,发送一个确认报文,然后自己变为TIME_WAIT,而不是立马关闭,因为客户端发送的确认报文可能会丢失,丢失的话服务器就会重传一个FIN,也就是释放连接报文,这个时候客户端必须还没关闭。当服务器接受到确认报文后,服务器就进入CLOSE状态,也就是关闭了。但是由于上面说的这个原因,客户端必须等待一定的时间才能够进入CLOSE状态。
同时关闭连接
正常情况下,通信一方请求连接关闭,另一方响应连接关闭请求,并且被动关闭连接。但是若出现同时关闭连接请求时,通信双方均从 ESTABLISHED 状态转换为 FIN_WAIT_1状态。任意一方收到对方发来的 FIN 报文段后,其状态均由 FIN_WAIT_1转变到 CLOSING 状态,并发送最后的 ACK 数据段。当收到最后的ACK数据段后,状态转变化TIME_WAIT,在等待 2MSL 时间后进入到 CLOSED 状态,最终释放整个TCP传输连接。