检查 write()/send() 是不是可以无块地处理整个缓冲区,否则失败(没有部分写入)

Posted

技术标签:

【中文标题】检查 write()/send() 是不是可以无块地处理整个缓冲区,否则失败(没有部分写入)【英文标题】:Check that write()/send() can process whole buffer without block, fail otherwise (no partial write)检查 write()/send() 是否可以无块地处理整个缓冲区,否则失败(没有部分写入) 【发布时间】:2011-10-10 21:59:43 【问题描述】:

我正在使用 SOCK_SEQPACKET 连接,确保通过单个 write()/send() 调用发送整个缓冲区对我来说至关重要。我还在使用设备驱动程序,该驱动程序旨在通过单个调用处理完整的数据块。同时我想处理由于缓冲区溢出导致 write()/send() 阻塞的情况,即我想反馈当前的实现是否在这里遇到瓶颈。我正在使用 glibc,Linux 2.6。

我需要实现一个接受缓冲区的方法,它要么完全发送缓冲区,要么指示由于阻塞而失败(即系统缓冲区溢出)。

看起来使用 send(..., MSG_DONTWAIT)/fcntl(..., O_NONBLOCK) 不是解决方案,因为它们在报告 EWOULDBLOCK/EAGAIN 之前接受部分写入。有没有办法检查传出缓冲区中是否有足够的空间,或者是否有专门的 write-complete-or-fail 方法?

或者,是否可以通过其他方式检测阻塞?例如,timer+signal 似乎是一个选项,但我不喜欢为每次写入设置它的想法。

提前谢谢你。

【问题讨论】:

使用哪个域/协议? PF_INET 和 SCTP? Unix 套接字:fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 您确定非阻塞 SOCK_SEQPACKET unix 套接字允许部分发送吗?我使用了 Unix 数据报套接字(AF_UNIX+SOCK_DGRAM)。如果您尝试发送太大的消息,sendto(,,MSG_DONTWAIT,,) 的调用将失败并显示 errno EAGAIN 或 EMSGSIZE。并且接收者永远不会接收部分数据报。 到目前为止一切顺利,似乎 AF_UNIX+SOCK_SEQPACKET 不允许部分发送,它按预期返回 EAGAIN/EWOULDBLOCK。我不确定这是否是普遍行为。谢谢你的提示,我被“man”页面弄糊涂了,仍然会出现部分感觉。 【参考方案1】:

读取套接字上 SO_SNDLOWAT 的值将为您提供有关可以发送的消息大小的线索,而不会冒部分发送的风险。

【讨论】:

你确定吗? man 说:“指定缓冲区中的最小字节数,直到套接字层将数据传递给协议(SO_SNDLOWAT)......这两个(+RCVLOWAT)值被初始化为1。SO_SNDLOWAT在Linux上不可更改。” 这取决于实现; SO_SNDLOWAT 的一种用途是定义在 select() 将套接字 fd 标记为准备写入之前可用的缓冲区空间的最小值。因此,该大小(或更小)的消息将完整地传递给协议。但如果它固定为 1,那么我同意它没有用......【参考方案2】:

实验表明,在我的设置(glibc 2.11.1,内核 2.6.32-29)上没有发生部分发送,并且 EAGAIN/EWOULDBLOCK 由 send(...MSG_DONTWAIT) 返回,正如 AF_UNIX+SOL_SEQPACKET 套接字所预期的那样。

不确定行为是否具有普遍性。

【讨论】:

以上是关于检查 write()/send() 是不是可以无块地处理整个缓冲区,否则失败(没有部分写入)的主要内容,如果未能解决你的问题,请参考以下文章

socket编程·send和recv

boost async_write() 和 non_blocking socket.send() 之间的区别

Python InfluxDB2 - write_api.write(...) 如何检查是不是成功?

read() 和 recv() 以及 send() 和 write() 之间有啥区别?

在 express 中 res.send 和 res.write 有啥区别?

Boost::asio async_write_some 与 async_send