1

我正在使用域套接字(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;
}
4

2 回答 2

2

4864 + 3328 = 8192. 我的猜测是,在某些情况下,您正在背靠背传输两个 4864 字节的数据包,并且它在某处填充了 8 KB 内核缓冲区。IP_DONTFRAG不适用,因为这里不涉及 IP — 您看到的“碎片化”是通过完全不同的机制发生的。

如果您传输的所有数据都由数据包组成,那么最好使用数据报套接字 ( SOCK_DGRAM) 而不是流。send()当内核缓冲区没有足够的空间来存储整个数据包时,这应该会生成块,而不是允许部分写入,并且会使每个都recv()返回一个数据包,因此您不需要处理帧。

于 2013-03-18T23:52:48.543 回答
2

SOCK_STREAM 根据定义不保留消息边界。使用 SOCK_DGRAM 或 SOCK_SEQPACKET 重试:

http://man7.org/linux/man-pages/man7/unix.7.html

另一方面,请考虑您传递的消息可能大于您的体系结构页面大小。例如对于amd64,一个内存页是4K。如果出于任何原因这是一个问题,那么将数据包分成 2 个可能是有意义的。

但是请注意,对于数据包分段到达而言,这并不是真正的问题。在套接字的接收端有一个数据包组装器是很常见的。实施它有什么问题?

于 2013-03-19T00:08:44.717 回答