传输层的定义及端口号设置
传输层的定义及通信处理
- IP 报文首部格式的时候提到其中有一个协议字段(Protocol),用来标识网络层的上一层所采用的是哪一种传输层协议,根据这个字段的协议号就可以识别 IP 传输的数据部分究竟是 TCP 的内容还是 UDP 的内容。
- 传输层的 TCP 和 UDP,为了识别自己所传输的数据部分究竟是应该发送给应用层的哪个应用,也设定了这样的一个编号。
- 这些服务端程序在 UNIX 系统中叫守护进程,例如 Nginx、PHP-FPM、MySQL、Redis 等后台服务都是守护进程。
- 确认一个请求究竟发送给哪个服务端程序,可以通过所收到数据包的目标端口号进行识别。例如,当收到 TCP 的建立连接请求时,如果目标端口号是 80,则转给 Nginx/Apache,然后 Nginx/Apache 会继续对该连接上的通信传输进行处理,比如如果请求的是静态资源则可以直接返回,如果是动态请求需要继续转发给相应的 CGI 处理程序进行处理,比如 PHP-FPM。
- 传输协议 TCP 和 UDP 都是通过接收数据中的目标端口号识别目标处理程序。
端口号定义及设置
数据链路和网络层中的地址,分别指的是 MAC 地址和 IP 地址,前者用来识别同一链路中的不同计算机,后者用来识别计算机网路中互连的主机和路由器。在传输层中也有类似于地址的概念,那就是端口号,端口号用来识别同一台计算机中进行通信的不同应用程序,因此,它也被称为程序地址。
在实际进行通信时,需要事先绑定端口号。端口号的有效范围介于 0~65535 之间。确定端口号的方式分为两种:
- 标准既定的端口号(静态方法):每个应用程序都有指定的端口号,但也不是任意指定,其中低于 1024 的端口号一般是系统预留的知名端口号,比如 HTTP 端口号一般是 80,SSH 端口号一般是 22,FTP 端口号一般是 21,SMTP 端口号一般是 25 等等。除此之外,还有一些端口号也被正式注册,它们分布在 1024 到 49151 之间,比如 MySQL 端口号是 3306,Redis 端口号是 6379,Memcache 端口号是 11211。
- 时序分配法(动态方法):服务端必须确定监听端口号,但是客户端不需要确定端口号,可以交由操作系统动态分配,操作系统可以为每个应用程序分配互不冲突的端口号,系统分配的端口号范围介于 49152 到 65535 之间。
我们在编写自己的服务端应用程序设置监听端口号时,一般只需要高于 1024 并避开系统已安装的其它应用程序已经注册的端口号即可。
UDP 协议
- UDP 提供面向无连接的通信服务,是不具有可靠性的数据报协议。
- UDP 虽然可以确保发送消息的大小,但不能保证消息一定会到达,传输途中如果出现丢包,UDP 不负责进行重发。因此,应用有时候会根据自己的需要进行重发处理。甚至当包的到达顺序错乱时,UDP 也没有纠正功能。
- UDP 也不提供复杂的控制机制,即使出现网络拥堵,UDP 也无法进行流量控制等避免避免网络拥塞的行为。
- 由于 UDP 面向无连接,可以随时发送数据,本身的处理逻辑简单高效,因此,常用于以下场景:
- 包总量较少(比如DNS、DHCP就是基于UDP协议进行通信)
- 视频、音频等多媒体即时通信(少量的丢包不影响通信质量)
- 广播通信(广播、多播)
UDP 报文首部由源端口号、目标端口号、包长和校验和组成:
- 源端口号:发送端端口号,该字段有可能为空(不需要返回的通信)
- 目标端口号:接收端端口号
- 包长度:UDP 首部的长度跟数据的长度之和
- 校验和:为了提供可靠的 UDP 首部和数据而设计,不一定会用到
TCP协议
TCP 协议与 UDP 协议的主要区别如下:
- TCP 提供面向连接的可靠传输,UDP 则提供面向无连接的不可靠传输,不保证不丢包,也不保证包按顺序到达,而 TCP 可以保证
- TCP 有拥塞控制,可以通过窗口控制对网络流量进行控制,避免网络拥塞,UDP 则不会。
TCP 协议主要用于可靠传输,而 UDP 主要用于对高速传输或者对实时性要求较高的通信或广播通信。我们日常比较熟悉的 HTTP 协议就是基于 TCP 协议的,此外,用于远程连接的 SSH 协议、发送邮件的 SMTP 协议、传输文件的 FTP 协议都是需要建立连接的可靠传输,也都是基于 TCP 协议。
TCP报文首部格式
TCP 中没有表示包长度和数据长度的字段,可由 IP 层获取 TCP 的包长度,由 TCP 的包长可获知数据的长度。
- 源端口号:发送端端口号
- 目标端口号:接收端端口号
- 序列号:发送数据的位置,每发送一次数据,就累加一次该数据字节数的大小,序列号不会从0或1开始,而是在建立连接时由计算机生成的随机数作为初始值,通过 SYN 包发送给接收端主机。此外,建立连接和断开连接时虽然不传输数据,但也会作为一个字节增加对应的序列号
- 确认应答号:下一次应该接收到的数据的序列号,实际上,它是指已经收到确认应答号减一为止的数据
- 数据偏移:TCP 所传输的数据部分应该从 TCP 包的哪个位置开始计算,也可以将其看作 TCP 包的长度,单位为4字节
- 保留:为了以后扩展用
- 控制位:字段长8位,每一位从左到右分别是CWR、ECE、URG、ACK、PSH、RST、SYN、FIN- CWR(Congestion Window Reduced):与后面的ECE都是用于IP首部的ECN字段,为1时通知对方已将拥塞窗口缩小
- ECE(ECN-Echo):置为1时通知通信对方,从对方到这边的网络有拥堵
- URG(Urgent Flag):该位为1时,表示包中有需要紧急处理的数据
- ACK(Acknowledgement Flag):该位为1时,确认应答的字段变为有效
- PSH(Push Flag):该位为1时,表示需要将收到的数据立即传给上层应用协议,为0时则表示先不上传而是缓存起来
- RST(Reset Flag):该位为1时,表示TCP连接中出现异常必须强制断开连接
- SYN(Synchronize Flag):该位为1时,表示希望建立连接,并在序列号字段进行序列号初始值的设定
- FIN(Fin Flag):该位为1时,表示今后不会再有数据发送,希望断开连接
- 用于通知从相同 TCP 首部的确认应答号所指位置开始能够接收的数据大小
- 校验和:TCP和UDP一样,在计算校验和时使用伪首部,主要用于检查路由器内存故障或程序漏洞导致的数据破坏
- 紧急指针:只有在 URG 控制位为1时有效,该字段数值表示本报文段中紧急数据的指针
- 选项:用于提高 TCP 的传输性能
- 填充:选项长度不足32位整数倍时将其补齐到32位整数倍
通过三次握手建立连接
- TCP 提供面向有连接的通信传输,面向有连接是指在数据通信开始之前先做好通信两端之间的准备工作。
- TCP 的连接建立,我们常常称为三次握手。三次握手的目的是为了确保对连接双方来说,链路都是通的。
- 三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是 TCP 包序列号的问题。每个连接都要有不同的序列号,这个序列号的起始序号是随着时间变化的。
- 建立了连接之后,为了维护这个连接,双方都要维护一个状态机,在连接建立的过程中,双方的状态变化时序图如下:
注:在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做确认应答(ACK)。 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。至此,就建立起了客户端与服务端的连接,双方可以进行数据传输了。
如何实现可靠的传输
- TCP 通过肯定的确认应答(ACK)实现可靠的数据传输,当发送端将数据发出之后会等待对端的确认应答。如果确认应答,说明数据已经成功达到对端。反之,则数据有可能丢失。在一定时间内没有等到确认应答,发送端就认为数据已经丢失,并进行重发。因此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。
- 未收到确认应答不一定是数据一定丢失,有可能是对方已经收到数据,只是返回的确认应答在途中丢失,这种情况也会导致发送端因为没有收到确认应答而重新发送数据。
- 此外,也有可能因为一些其他原因导致确认应答延迟到达,在源主机重发数据以后才到达的情况也屡见不鲜,此时,对源主机没啥影响,但是对目标主机来说,它会反复收到同样的数据。而为了对上层应用提供可靠的传输,必须放弃重复的数据包。为此,就必须引入一种机制,能够识别是否已经接收数据,又能够判断是否需要接收。
- 上述这些确认应答处理、重发控制以及重复控制等功能都可以通过序列号实现,序列号是按顺序给发送数据的每一个字节都标上号码的编号。
- 接收端查询接收数据 TCP 首部中的序列号和数据长度,将自己下一步应该接收的序列号作为确认应答发送回去,就这样,通过序列号和确认应答号,可以有效解决顺序问题和丢包问题,TCP 就可以实现可靠传输:
原文来自Laravel学院