以下是有关我遇到的问题的一些背景:
我有一个流server_fd = socket(AF_UNIX, SOCK_STREAM, 0)
类型的 unix 套接字。在服务器端,套接字通过listen(server_fd, 128)
listen(2)
,并绑定到处理EPOLLIN
的 epoll 处理程序。当从所述套接字读取(使用 epoll 回调)时,我使用accept(2)
为客户端创建一个新的套接字,该套接字绑定到它自己的 epoll 处理EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR
。到目前为止相当标准。
问题是这样的:
由于服务器端的数据分散在多个源中,并且目的是让客户端以整洁的包形式获取数据,因此我做了这样的事情:
void **data_portions = NULL;
size_t *sz_data_portions = NULL;
size_t cnt_data_portions = 0;
/// ... Fill the variables above based on the needs
fill_data(&data_portions, &sz_data_portions, &cnt_data_portions);
for (size_t i = 0; i < cnt_data_portions; i++) {
int flags = 0;
if (i < cnt_data_portions - 1) {
flags = MSG_MORE;
}
send(fd_peer, data_portions[i], sz_data_portions[i], flags);
}
服务器有效地将data_portions
内容一个接一个地发送,所有内容都带有标志MSG_MORE
除了最后一个没有标志的内容。 可以说,服务器成功发送了所有数据。
现在让我们假设特定的场景,即cnt_data_portions = 2
和sz_data_portions = {128, 32}
.这意味着有两个调用send(2)
。第一个有len = 128
和flags = MSG_MORE
,第二个有len = 32
和flags = 0
。
因为我在第一次调用中使用了MSG_MORE
,所以在客户端,我希望能够使用recv(2)
一次读取128 + 32 = 160
个字节。但是,客户端只能从套接字读取128
字节。这违背了MSG_MORE
的精神。我不明白为什么客户端不能一次读取所有160
字节。
更多信息:
- 套接字是非阻塞的(
O_NONBLOCK
),允许地址重用(SO_REUSEADDR
设置为1)。 - 太阳之路
"/tmp/test-socket"
. bind(2)
用于将套接字绑定到服务器端的 Sunpath,connect(2)
用于客户端SO_RCVBUF
和SO_SNDBUF
都设置为 1 MB- 我将调试放在服务器和客户端。我什至确保在客户端尝试读取任何内容之前发送所有数据部分。尽管如此,同样的问题还是发生了。
更新
我试图与MSG_MORE
send
的原因是我想将来自不同来源的数据收集到一条消息中。为此,我切换到sendmsg
.
从 linuxunix(7)
手册页:
UNIX 域套接字不支持
send(2)
MSG_MORE
标志。
并且还感兴趣:
SO_SNDBUF
套接字选项对 UNIX 域套接字有影响,但SO_RCVBUF
选项不起作用。
所以在你的代码中使用MSG_MORE
是没有意义的;它适用于TCP和UDP套接字。
此外,流上的读取次数(无论是 TCP 套接字、UNIX 域套接字、管道等)与远端的写入次数无关。您必须在使用流的更高级别协议中包含消息边界等内容。如果这样做是一个问题,你可以考虑一个SOCK_SEQPACKET
的unix套接字,结合writev(2)
来发送分散的数据(我认为这会导致所有数据都在一个数据包中)。