域套接字碎片化

  • 本文关键字:碎片 套接字 sockets
  • 更新时间 :
  • 英文 :


我使用域套接字(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()只返回一个数据包,因此您不需要处理分帧。

最新更新