7

这更多是一种观察,也是对处理这种情况的最佳方法的建议。

我有两个线程,一个只是泵入数据,另一个接收数据并在发送另一个套接字之前做很多工作。两个线程都通过域套接字连接。这里使用的协议是 UDP。我不想使用 TCP,因为它是基于流的,这意味着如果队列中的空间很小,我的数据就会被拆分和发送。这很糟糕,因为我正在发送不应该拆分的数据。因此我使用了 DGRAM。有趣的是,当发送线程通过抽取如此多的数据压倒了接收线程时,在某些时候域套接字缓冲区被填满并且 sendto() 返回 ENOBUFS。我认为如果发生这种情况, sendto() 将阻塞,直到缓冲区可用。这将是我想要的行为。然而,情况似乎并非如此。我以一种相当奇怪的方式解决了这个问题。

  • CPU Yield 方法 如果我得到 ENOBUFS,我会执行 sched_yield(); 因为 OSX 中没有 pthread_yield() 。之后,我尝试再次重新发送。如果失败了,我会继续做同样的事情,直到它被采取。这很糟糕,因为我只是在做一些无用的事情而浪费 CPU 周期。如果 sendto() 被阻止,我会很高兴。

  • 睡眠方法我尝试使用 sleep(1) 而不是 sched_yield() 来解决相同的问题,但这没有用,因为 sleep() 会使我的进程进入睡眠状态,而不仅仅是发送线程。

他们两个似乎都不适合我,而且我已经没有选择了。有人可以建议处理这个问题的最佳方法是什么吗?是否有一些我不知道的巧妙技巧可以减少不必要的 CPU 周期?顺便说一句,根据这个讨论http://lists.freebsd.org/pipermail/freebsd-hackers/2004-January/005385.html ,手册页上关于 sentto() 的内容是错误的

内核中的 Upd 代码:

The udp_output function in /sys/netinet/udp_usrreq.c, seems clear:

         /*
          * Calculate data length and get a mbuf
          * for UDP and IP headers.
          */
         M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
         if (m == 0) {
                 error = ENOBUFS;
                 if (addr)
                         splx(s);
                 goto release;
         }
4

2 回答 2

1

我不确定为什么 sendto() 没有阻止你……但你可以在每次调用 sendto() 之前尝试调用这个函数:

#include <stdio.h>
#include <sys/select.h>

// Won't return until there is space available on the socket for writing
void WaitUntilSocketIsReadyForWrite(int socketFD)
{
   fd_set writeSet;
   FD_ZERO(&writeSet);
   FD_SET(socketFD, &writeSet);
   if (select(socketFD+1, NULL, &writeSet, NULL, NULL) < 0) perror("select");
}

顺便说一句,您尝试发送的数据包有多大?

于 2013-05-15T03:40:19.857 回答
0

OS X 上的 sendto() 确实是非阻塞的(即 M_DONTWAIT 标志)。

我建议您使用基于流的连接,并使用函数的 MSG_WAITALL 标志接收另一端的全部数据recv。如果您的数据具有比它简单的结构严格的结构,只需将正确的大小传递给recv. 如果不是首先发送一些固定大小的控制数据包,其中包含下一个数据块的大小,然后是数据本身。在接收方,您将等待固定大小的控制包,而不是来自控制包的大小数据。

于 2013-05-15T09:40:52.550 回答