我正在使用域套接字(AF_UNIX)在两个线程之间进行通信以进行进程间通信。这被选择与libev一起使用:我在域套接字的recv端使用它。除了我发送的数据是常量4864字节之外,这非常有效。我无法承受这些数据的碎片化。我一直认为域套接字不会对数据进行分段,但事实证明它确实存在。当通信在线程之间达到峰值时,我会观察以下内容
Thread 1:
SEND = 4864 actual size = 4864
Thread 2:
READ = 3328 actual size = 4864
Thread 1:
SEND = 4864 actual size = 4864
Thread 2:
READ = 1536 actual size = 4864
如您所见,线程2以片段形式接收数据(3328 + 1536)。这对我的应用来说真的很糟糕。无论如何我们可以让它不碎片吗?我知道IP_DONTFRAG只能设置为AF_INET系列吗?有人可以建议替代方案吗?
更新:发送代码
ssize_t
socket_domain_writer_dgram_send(int *domain_sd, domain_packet_t *pkt) {
struct sockaddr_un remote;
unsigned long len = 0;
ssize_t ret = 0;
memset(&remote, '\0', sizeof(struct sockaddr_un));
remote.sun_family = AF_UNIX;
strncpy(remote.sun_path, DOMAIN_SOCK_PATH, strlen(DOMAIN_SOCK_PATH));
len = strlen(remote.sun_path) + sizeof(remote.sun_family) + 1;
ret = sendto(*domain_sd, pkt, sizeof(*pkt), 0, (struct sockaddr *)&remote, sizeof(struct sockaddr_un));
if (ret == -1) {
bps_log(BPS_LOGGER_RD, ASL_LEVEL_ERR, "Domain writer could not connect send packets", errno);
}
return ret;
}
答案 0 :(得分:2)
4864 + 3328 = 8192
。我的猜测是你在某些情况下背靠背传输两个4864字节的数据包,而且它正在某个地方填充一个8 KB的内核缓冲区。 IP_DONTFRAG
不适用,因为此处不涉及IP - 您所看到的“碎片化”是通过完全不同的机制发生的。
如果您传输的所有数据都包含数据包,那么最好使用数据报套接字(SOCK_DGRAM
)而不是流。当内核缓冲区没有足够的空间来存储整个数据包而不允许部分写入时,这应该使send()
阻塞,并使每个recv()
返回一个数据包,所以你不需要处理框架。
答案 1 :(得分:2)
根据定义,SOCK_STREAM不保留消息边界。再次尝试使用SOCK_DGRAM或SOCK_SEQPACKET:
http://man7.org/linux/man-pages/man7/unix.7.html
另一方面,请考虑您可能传递的邮件大于您的体系结构页面大小。例如,对于amd64,内存页面是4K。如果由于任何原因这是一个问题,将数据包拆分为2可能是有意义的。
但请注意,对于数据包到达碎片而言,这不是一个真正的问题。在套接字的接收端有一个数据包汇编程序是很常见的。实施它有什么问题?