我使用域套接字(AF_UNIX)在两个线程之间进行进程间通信。选择它是为了与libev一起很好地工作:我在域套接字的接收端使用它。这工作得非常好,除了我发送的数据是恒定的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 family?有人能建议一个替代方案吗?
更新:sendto code
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, ' ', 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;
}
SOCK_STREAM根据定义不保留消息边界。再试一次SOCK_DGRAM或SOCK_SEQPACKET:
http://man7.org/linux/man-pages/man7/unix.7.html另一方面,要考虑传递的消息可能大于体系结构页面的大小。例如,对于amd64,一个内存页是4K。如果由于任何原因这是一个问题,那么将数据包分成2个是有意义的。
注意,但是,这不是一个真正的问题,数据包到达碎片。在套接字的接收端通常有一个数据包汇编器。实现它有什么问题?
4864 + 3328 = 8192
。我的猜测是,在某些情况下,您正在背靠背地传输两个4864字节的数据包,并且它在某处填充了一个8 KB的内核缓冲区。IP_DONTFRAG
不适用,因为这里不涉及IP—您所看到的"碎片化"是通过完全不同的机制发生的。
如果您传输的所有数据都由数据包组成,则最好使用数据报套接字(SOCK_DGRAM
)而不是流。当内核缓冲区没有足够的空间来存储整个数据包时,这将使send()
阻塞,而不是允许部分写through,并且将使每个recv()
只返回一个数据包,因此您不需要处理分帧。