传输层协议负责将数据从发送端传输到接收端。
一、再谈端口号
端口号标识了一个主机上进行通信的不同的应用程序。在 TCP/IP 协议中,用 "源IP","源端口号","目的 IP","目的端口号","协议号" 这样一个五元组来标识一个通信(可以通过 netstat -n 查看)。

1.1 端口号范围划分
•0 - 1023: 知名端口号,HTTP、FTP、SSH 等这些广为使用的应用层协议的端口号都是固定的。
• **1024 - 65535:**操作系统动态分配的端口号,客户端程序的端口号就是由操作系统从这个范围分配的。
有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:
• ssh 服务器使用 22 端口
• ftp 服务器使用 21 端口
• telnet 服务器使用 23 端口
• http 服务器使用 80 端口
• https 服务器使用 443
在linux下我们可以通过 cat /etc/services来查看知名端口号。在我们自己写程序使用端口号时要避开这些知名端口号。
一个进程可以绑定多个端口号,一个端口号只能被一个进程绑定。
二、UDP协议
协议就是一种约定,协议本质就是通信双方约定好的格式化字段,因此协议就是struct或者class定义的结构。操作系统中的UDP和TCP这些传输层协议及它们以下的协议在操作系统中就是纯C的结构体,但是操作系统出于效率的考量,它选择了在代码里直接传输二进制的结构体对象,可以理解为没有进行序列化与反序列化,也可以理解为直接以二进制的方式做的序列化和反序列化。添加报头实际上就是定义了一个新的struct的结构体对象,然后把上一层的结构体对象与我们这一层的报头合并起来。
2.1 UDP协议格式

UDP是如何解包,如何分用的呢?
UDP在解包时直接读取前八个字节即它的报头,剩下的部分就是有效载荷。而后通过16位的目的端口号分用的。
UDP是如何保证报文完整性的?
固定的报头长度+16位UDP长度(用于标识有效载荷和报头长度),再通过UDP16位校验和进行校验,成功则向应用层交付报文,失败就要丢弃掉。
我们注意到,在UDP 协议首部中有一个 16 位的最大长度,也就是说一个 UDP 能传输的数据最大长度是 64K (包含 UDP 首部)。然而 64K 在当今的互联网环境下是一个非常小的数字,如果我们需要传输的数据超过 64K,就需要在应用层手动的分包多次发送,并在接收端手动拼装。
下面是在Linux中的UDP结构:
cpp
struct udphdr
{
__u16 source;
__u16 dest;
__u16 len;
__u16 check;
};
2.2 UDP的特点
• **无连接:**知道对端的 IP 和端口号就直接进行传输,不需要建立连接。
• **不可靠:**没有确认机制,也没有重传机制。如果因为网络故障该段无法发到对方,UDP 协议层也不会给应用层返回任何错误信息。
•**面向数据报:**不能够灵活的控制读写数据的次数和数量。应用层交给 UDP 多长的报文,UDP 原样发送,既不会拆分也不会合并。
2.3 UDP的缓冲区
• UDP 没有真正意义上的发送缓冲区,调用 sendto 会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作。
• **UDP 具有接收缓冲区。**但是这个接收缓冲区不能保证收到的 UDP 报的顺序和发送 UDP 报的顺序一致;缓冲区满了之后到达的 UDP 数据会被丢弃。
UDP 的 socket 既能读也能写,这个概念叫做 全双工。
2.4 基于 UDP 的应用层协议
• **NFS:**网络文件系统
• **TFTP:**简单文件传输协议
• **DHCP:**动态主机配置协议
• **BOOTP:**启动协议(用于无盘设备启动)
• **DNS:**域名解析协议
• 自定义的基于UDP的应用层协议