MSG_MORE unix stream socket send 对 peer 的 recv 无效
MSG_MORE in send of unix stream socket is not effective on the peer's recv
以下是关于我遇到的问题的一些背景信息:
我有一个流类型的 unix 套接字 server_fd = socket(AF_UNIX, SOCK_STREAM, 0)
。在服务器端,套接字通过 listen(server_fd, 128)
listen(2)
ed 并绑定到处理 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
- 我在服务器端和客户端都调试了一个。我什至确保在客户端尝试读取任何内容之前发送所有数据部分。仍然,同样的问题发生了。
更新
我尝试 send
和 MSG_MORE
的原因是我想将来自不同来源的数据收集到一条消息中。为此,我切换到 sendmsg
.
来自linux unix(7)
man page:
The send(2)
MSG_MORE
flag is not supported by UNIX domain sockets.
也很感兴趣:
The SO_SNDBUF
socket option does have an effect for UNIX domain sockets, but the SO_RCVBUF
option does not.
所以在你的代码中使用 MSG_MORE
是没有意义的;它用于 TCP 和 UDP 套接字。
此外,流(无论是 TCP 套接字、UNIX 域套接字、管道等)的读取次数与远端的写入次数无关。您必须在使用流的更高级别的协议中包含诸如消息边界之类的内容。如果这样做是个问题,您可能会查看 SOCK_SEQPACKET
unix 套接字,结合 writev(2)
发送分散数据(我 认为 这会导致所有数据在一个数据包中)。
以下是关于我遇到的问题的一些背景信息:
我有一个流类型的 unix 套接字 server_fd = socket(AF_UNIX, SOCK_STREAM, 0)
。在服务器端,套接字通过 listen(server_fd, 128)
listen(2)
ed 并绑定到处理 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- 我在服务器端和客户端都调试了一个。我什至确保在客户端尝试读取任何内容之前发送所有数据部分。仍然,同样的问题发生了。
更新
我尝试 send
和 MSG_MORE
的原因是我想将来自不同来源的数据收集到一条消息中。为此,我切换到 sendmsg
.
来自linux unix(7)
man page:
The
send(2)
MSG_MORE
flag is not supported by UNIX domain sockets.
也很感兴趣:
The
SO_SNDBUF
socket option does have an effect for UNIX domain sockets, but theSO_RCVBUF
option does not.
所以在你的代码中使用 MSG_MORE
是没有意义的;它用于 TCP 和 UDP 套接字。
此外,流(无论是 TCP 套接字、UNIX 域套接字、管道等)的读取次数与远端的写入次数无关。您必须在使用流的更高级别的协议中包含诸如消息边界之类的内容。如果这样做是个问题,您可能会查看 SOCK_SEQPACKET
unix 套接字,结合 writev(2)
发送分散数据(我 认为 这会导致所有数据在一个数据包中)。