> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/4.3-bsd-unix-cao-zuo-xi-tong-she-ji-yu-shi-xian/di-si-bu-fen-jin-cheng-jian-tong-xin/12.2.-yong-hu-shu-ju-bao-xie-yi-udp.md).

# 12.2 用户数据报协议（UDP）

用户数据报协议（UDP）是一个简单的不可靠数据报协议，只提供对等编址和可选的数据校验和。它的协议头极其简单，只含源和目的端口号、数据报长度和数据校验和。数据报的主机地址由 IP 伪头部提供。

**初始化**

Internet 域中创建新数据报套接字时，套接字层找到 UDP 的协议开关表项，带套接字参数调用 `udp_usrreq()` 例程的 PRU\_ATTACH 入口。UDP 用 `in_pcballoc()` 在当前套接字链表上创建新的协议控制块，并设置套接字发送和接收缓冲的默认上限。数据报从不放进发送缓冲，但上限用作数据报大小的上限；UDP 的协议开关表项含 PR\_ATOMIC 标志，要求一次发送操作的全部数据一次呈交协议。

应用程序若想绑定端口号——例如某个数据报服务的著名端口——就调用 `bind` 系统调用。这一请求以 PRU\_BIND 请求到达 `udp_usrreq()`。绑定还可以指定具体的主机地址，必须是本主机某接口的地址；否则地址留空，输入时匹配任何本地地址，每次输出操作按需选择地址。绑定由 `in_pcbbind()` 完成：验证所选端口号（或地址加端口）未被使用，然后记录关联的本地部分。

要发送数据报，系统必须知道关联的远程部分。地址和端口可以随每次发送操作用 `sendto` 或 `sendmsg` 指定，也可以预先用 `connect` 系统调用规定。两种情况下，UDP 都用 `in_pcbconnect()` 函数记录目的地址和端口。本地地址尚未绑定且找到目的地的路由时，外出接口的地址用作本地地址。本地端口号未绑定时，此时选一个。

**输出**

发送数据的系统调用以 PRU\_SEND 请求带装着数据报数据的 mbuf 链到达 `udp_usrreq()`。调用若提供了目的地地址，地址一并传递；否则用先前 `connect` 调用的地址。实际输出操作由 `udp_output()` 完成：

```c
error = udp_output(inp, m);
        struct inpcb *inp;
        struct mbuf *m;
```

`inp` 是 Internet 协议控制块，`m` 是装待发数据的 mbuf 链。UDP 简单地前置自己的头部、填好本头字段和一个 IP 原型头部的字段、计算校验和，然后把分组交给 IP 模块输出：

```c
error = ip_output(m, opt, ro, flags);
        struct mbuf *m, *opt;
        struct route *ro;
        int flags;
```

对 IP 输出例程的调用比 UDP 的复杂，因为 IP 例程不能指望有装着当前发送者和目的地信息的协议控制块。`m` 参数指明待发数据；`opt` 参数可指定应放进 IP 分组头的一组 IP 选项。套接字的 IP 选项可用 `setsockopt` 系统调用指定 IP 协议层和 IP\_OPTIONS 选项来设置。这些选项存在单独的 mbuf 中，指向该 mbuf 的指针存在套接字的协议控制块里，随每个发出的分组传给 `ip_output()`。`ro` 参数可选；UDP 传套接字协议控制块中路由表项的指针。IP 会确定路由并留在路由结构中，供后续调用重用。最后的参数 `flags` 指明是否允许用户发送广播消息、以及是否为所发消息做路由（见 12.3 节）。底层硬件不支持广播传输时，广播标志可能无关紧要。

**输入**

所有直接叠在 IP 之上的 Internet 传输协议，从 IP 接收输入分组时都用如下调用约定：

```c
(void) (*pr_input)(m, ifp);
        struct mbuf *m;
        struct ifnet *ifp;
```

传入的每条 mbuf 链是协议模块待处理的一个分组；收到分组的接口作为第二参数传递。UDP 输入例程 `udp_input()` 是协议输入例程的典型。它先验证分组至少有 IP 头加 UDP 头那么长，用 `m_pullup()` 使头部连续；再检查分组长度正确、有校验和时对数据做校验。任何测试失败，分组径直丢弃。最后由 `in_pcblookup()` 按分组中的地址和端口号定位接收数据的套接字的协议控制块。可能有多个控制块同本地端口号而本地或远程地址不同：此时选匹配最好的。精确关联匹配最好；不存在时，本地端口号正确而本地地址、远程端口号或远程地址未指定的套接字可以匹配。本地和/或远程地址未指定的控制块由此充当 **通配**（wildcard），无精确匹配时接收发往其端口的分组。找到控制块的话，数据和收到分组的源地址用 `sbappendaddr()` 放进所指套接字的接收缓冲。否则，找不到接收者且分组不是发往广播地址时，向数据报的始发者发一条 ICMP 端口不可达错误报文。

**控制操作**

UDP 支持的控制操作寥寥。4.3BSD 中它不支持选项，对 `pr_ctloutput()` 入口的调用直接转给 IP。它有一个简单的 `pr_ctlinput()` 例程，接收任何异步错误的通知。有些错误只是让缓存的路由失效；另一些传给有指定目的地的任何数据报套接字——只有经 `connect` 调用固定了目的地的套接字才能被异步通知错误。这类错误记进相应套接字，并发出套接字唤醒，以防进程正在 select 或入睡等输入。

UDP 数据报套接字关闭时，带 PRU\_DETACH 请求调用 `udp_usrreq()`。协议控制块及其内容用 `in_pcbdetach()` 径直删除，无须其他处理。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://book.bsdcn.org/4.3-bsd-unix-cao-zuo-xi-tong-she-ji-yu-shi-xian/di-si-bu-fen-jin-cheng-jian-tong-xin/12.2.-yong-hu-shu-ju-bao-xie-yi-udp.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
