ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【LINUX网络】UDP协议基础原理

【LINUX网络】UDP协议基础原理 在已经简单实现了UDP\TCP\HTTP的简单服务之后我们重谈一些理论内容理解UDP和TCP的底层。UDP比TCP简单很多我们逐步学习。1. 重新理解端口号端口号(Port)标识了一个主机上进行通信的不同的应用程序;在 TCP/IP协议中,用源IP, 源端口号, 目的IP, 目的端口号, 协议号这样一个五元组来标识一个通信(可以通过netstat -n查看)什么是协议号协议号Protocol Number是 IP 数据报中的一个字段用于标识该数据报所使用的传输层协议。它是一个 8 位的数字位于 IP 数据报的头部。常见的协议号TCP传输控制协议协议号为6。TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议广泛用于互联网中的数据传输。UDP用户数据报协议协议号为17。UDP 是一种无连接的、不可靠的传输层协议适用于对实时性要求较高的应用如视频流、语音通信等。ICMP互联网控制消息协议协议号为1。ICMP 主要用于发送错误消息和相关信息帮助网络管理员诊断网络问题例如ping命令就是基于 ICMP 协议实现的。帮助读者复习一下对于TCP/IP协议簇一般我们只讨论应用层、传输层、网络层。UDP和TCP是共同位于传输层的协议传输层主要是负责两台主机之间数据的收发如上图今天有三个http请求客户端B一个A两个在 TCP/IP 网络中IP 地址只能定位到具体的某台电脑比如 172.20.100.32但电脑里运行着很多程序浏览器、QQ、游戏等。端口号Port就是电脑内部的“门牌号”。服务器端Web 服务通常固定在 80 号端口就像邮局的大门。客户端当你打开浏览器访问网页时操作系统会随机分配一个临时端口比如图中的 2001、2002作为接收回信的“窗口”。0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议,他们的端口号都是固定的.•1024 - 65535: 操作系统动态分配的端口号.客户端程序的端口号,就是由操作系统从这个范围分配的其中最出名的有80是HTTP 443是HTTPS 22是SSH 23是TELNET2. UDP结构从两个问题引入一个进程是否可以bind多个端口号?可以一个进程可以绑定多个端口号。这在实际应用中是比较常见的尤其是在多线程或多进程的服务器程序中。例如一个服务器进程可能需要监听多个不同的服务端口以提供不同的服务。假设你有一个 Web 服务器进程它可能需要监听以下端口80用于 HTTP 服务。443用于 HTTPS 服务。8080用于开发环境中的 HTTP 服务。一个端口号是否可以被多个进程bind?一般情况下不可以在大多数操作系统中一个端口号在同一时间只能被一个进程绑定。如果尝试让多个进程绑定同一个端口号操作系统会拒绝后续的绑定请求并返回一个错误很类似于函数的概念端口号是一种x自变量进程是一个y因变量。同一个y可以被多个端口号映射而一个x不能去映射多个y。前置背景操作系统是C语言写的所以协议都是结构体struct不可能是class。但是UDP的报文是一定需要被管理起来的所以采用一个结构体管理UDP的报头一共是4个双字节也就是8B。因为UDP是应用层之下、网络层之上的协议需要记录这个报文来自哪个端口以及端口背后对应的进程xxxServer.cc16位源端口号记录的就是发送这个报文的端口号16位目的端口号记录的就是报文的目标主机的端口号。如果对之前的demo有印象的话端口号我们使用的类型就是uint16_t表示无符号的 16 位整数 。接着是16位UDP数据报的总长度以及16位的校验和。发送方的校验和会将原来的UDP报头和一个伪报头相拼接得到一个假数据报经过一系列计算得到一个数并取反接收方照着相同的办法计算但是不取反最后相加检查是否为0。但UDP本身是一种无连接的、不可靠的传输协议它本身不提供错误恢复机制。因此当接收方发现校验和不匹配时它只能根据应用层的逻辑来处理这种情况。我们注意到, UDP协议首部中有一个16位的最大长度.也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部).然而64K在当今的互联网环境下,是一个非常小的数字.如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装3. 理解UDP在传输中的位置之前讲过网络每一层之间就是通过不停的解包和分用来垂直传递的。面对现在的UDP报头我们可以理解1、之所以现在的报文里根本没有IP的数据是因为IP的信息是在网络层才会封装进去的。2、UDP的解包就是直接读取前八个字节分用解包之后该往哪里传就使用UDP报头中的第二个16位信息——目的端口即可。学习底层代码不难发现udp的header的确非常简单并且确实是结构体。4. sk_buff1.sk_buff的作用Linux内核会分配一个内存空间用于接收网络数据它贯穿整个协议栈、被所有层共享使用的通用数据载体。它本质上是内核为网络数据包分配的一块连续内存区域。sk_buff就是用于管理这片内存的。sk_buffsocket buffer是 Linux 内核中用于表示网络数据包的数据结构。它不仅存储了网络数据包的实际数据还包含了与数据包相关的各种元数据如数据包的长度、协议类型、时间戳等。sk_buff是网络协议栈中数据包处理的基础贯穿了从网络设备驱动到协议栈各个层次的整个数据处理流程。操作系统为了管理这个sk_buff采用的也是先组织、再管理所以所有的sk_buff都是被一个双链表管理起来的。2.sk_buff的主要字段sk_buff结构体定义在内核源码的include/linux/skbuff.h文件中。以下是一些重要的字段data和taildata指向数据包数据的起始位置可能会因为——例如TCP想留一个报头的位置而往后移动tail指向数据包数据的结束位置可能会因为不停的加数据而往后移动这两个指针用于管理数据包的实际数据部分。head和endhead指向sk_buff分配的内存块的起始位置全程不变。end指向sk_buff分配的内存块的结束位置全程不变。这两个指针用于管理sk_buff的内存分配范围。next和prev用于将sk_buff链接到链表中便于批量处理数据包。3.sk_buff的生命周期sk_buff的生命周期从数据包的接收开始贯穿整个网络协议栈直到数据包被处理完毕并释放。3.1数据包接收当网络设备如网卡接收到一个数据包时设备驱动程序会分配一个sk_buff并将数据包的数据复制到sk_buff的数据区域。然后驱动程序将sk_buff提交给网络协议栈进行处理。3.2协议栈处理数据包在协议栈中逐层处理每层协议如链路层、网络层、传输层都会对sk_buff进行操作分用和解包。例如链路层会解析以太网头部。网络层会解析 IP 头部。传输层会解析 TCP 或 UDP 头部。每层协议可能会修改sk_buff的字段如protocol和cb。3.3数据包发送当应用程序发送数据时内核会创建一个sk_buff并将数据复制到sk_buff的数据区域。然后协议栈逐层处理sk_buff最终将其传递给网络设备驱动程序由驱动程序将数据包发送到网络上。3.4释放一旦数据包处理完毕sk_buff会被释放释放其占用的内存资源。4.sk_buff的操作内核提供了许多函数来操作sk_buff以下是一些常见的操作数据操作skb_put(skb, len)在sk_buff的尾部添加len字节的空间并将tail指针向前移动。skb_pull(skb, len)从sk_buff的头部移除len字节的数据并将data指针向前移动。skb_push(skb, len)在sk_buff的头部添加len字节的空间并将data指针向后移动。链表操作skb_queue_head(queue, skb)将sk_buff添加到链表的头部。skb_dequeue(queue)从链表的头部移除一个sk_buff添加报头添加好报头之后就可以发往下一层5.UDP的特点UDP 传输过程可以类比为寄信无连接特性只需知道目标 IP 和端口号即可直接传输无需预先建立连接。不可靠性缺乏确认和重传机制若因网络故障导致传输失败UDP 协议层不会向应用层反馈任何错误信息如刚刚的校验码错误但是依然依赖应用层面向数据报无法灵活控制读写数据的次数和数量应用层交给 UDP 的报文会原样发送既不拆分也不合并示例传输 100 字节数据时发送端调用一次 sendto(100B)接收端必须对应调用一次 recvfrom(100B)不能分 10 次调用 recvfrom(每次 10B)UDP 缓冲区机制发送缓冲区不存在真正意义上的发送缓冲区调用 sendto 会直接交由内核处理带来的坏处是如果内核无法立即发送数据例如网络拥塞或套接字已关闭数据可能会丢失TCP拥有缓冲区其实就是为了处理数据、暂存数据保证数据不丢失。接收缓冲区用于存储收到的UDP报文不保证报文接收顺序与发送顺序一致缓冲区满载时新到达的 UDP 数据将被丢弃UDP socket 支持读写双向操作这种特性称为全双工通信。当然在现实网络世界中UDP没有想象的那么不可靠并且也更简单一点。TCP既然需要保证连接不出错一定会做更多工作因此一定会更慢。所以为了在一些需要效率并且又可以容忍一小部分数据丢失的情况下比如视频和音频直播。画面出现抖动、马赛克等就是UDP短暂的丢包了这些都是可以接受的。
返回列表