POSIX系统下UDP套接字write()返回值是否仅为size或-1?
write()的返回值行为解析 这个问题问得非常到位——很多人刚从TCP转向UDP开发时,都会对write()的行为产生类似疑问,毕竟TCP的write()经常出现短写(返回值小于请求的size),很容易形成思维定式。
核心结论
对于UDP套接字(SOCK_DGRAM类型),在POSIX系统上调用write(sock_fd, data, size)时,返回值只会是两种情况:
- 成功时返回
size:整个数据块被完整提交给内核处理(放入发送队列或直接发送) - 失败时返回
-1:数据完全没有被发送,同时errno会给出具体错误原因
也就是说,只要write()没有失败,你完全可以确定请求的整个数据块已经被内核接收处理,不需要像TCP那样检查返回值是否小于size再循环补发送。
背后的原因
这是由UDP的**面向消息(message-oriented)**核心特性决定的:
- UDP的数据传输是以完整的数据报为单位的,内核不会拆分你传入的数据块(除非你主动使用
sendmsg等做分散聚合操作,但write不会触发这种行为) - 如果你的数据块大小超过了路径MTU(最大传输单元),内核会直接返回
EMSGSIZE错误(返回-1),不会尝试发送部分数据 - POSIX标准对
SOCK_DGRAM套接字的send/write行为有明确的隐含规定:这类操作是原子性的,要么全成功,要么全失败,不存在中间状态
关于手册页的疑惑
你提到没在手册页找到明确说明,其实不少POSIX系统的write(2)或send(2)手册会在SOCK_DGRAM相关章节标注这一点,比如有些手册会写:"For SOCK_DGRAM sockets, the entire message shall be sent in a single operation." 可能是你没留意到这个细节,或者某些简化版手册省略了这部分描述。
有没有罕见的例外场景?
几乎不存在。唯一可能的“异常”都是系统级错误,比如:
- 套接字已经被关闭
- 系统内存不足,无法分配发送缓冲区
- 目标地址不可达(不过这种情况有时
write会先成功返回,后续的错误会通过recvfrom收到ICMP报文)
这些情况都会直接让write返回-1,不会出现返回值在0到size之间的情况。
额外提醒
需要注意的是,write返回成功不代表对方已经收到数据——UDP是不可靠协议,内核只是把数据报交给了网络栈,不保证对方能收到,也不保证数据报的顺序。但这和write的返回值无关,返回成功仅表示内核已经接受了你的发送请求。
内容的提问来源于stack exchange,提问作者Kristian Spangsege

