我正在编写一个Windows 7 visual c ++服务器应用程序,它应该接收3.6 MB / s的UDP数据报。 我有一个主线程,recvfrom()接收数据。套接字是一个非阻塞套接字,具有64kB接收缓冲区。如果套接字上没有收到数据,则线程执行sleep(1)。
我的问题是该线程占用了我的双核处理器的近50%,我不知道如何减少它。 Wireshark只使用了20%,所以我的主要目标是达到相似的百分比。
你有什么想法吗?
答案 0 :(得分:4)
您可以使用类似于select的方法来等待数据到达您的套接字或客户端决定关闭,而不是轮询您。
首先让你的套接字无阻塞:
u_long nonBlocking = 0;
WSAEventSelect(sock, NULL, 0);
ioctlsocket(sock, FIONBIO, &nonBlocking);
然后使用WSAWaitForMultipleEvents等待数据到达或您要取消recv:
int32_t MyRecv(THandle sock, WSAEVENT* recvCancelEvt,
uint8_t* buffer, uint32_t bufferBytes)
{
int32_t bytesReceived;
WSAEVENT evt;
DWORD ret;
HANDLE handles[2];
event = WSACreateEvent();
if (NULL == evt) {
return;
}
if (0 != WSAEventSelect(handle->iSocket, evt, FD_READ|FD_CLOSE)) {
WSACloseEvent(event);
return;
}
bytesReceived = recv(sock, (char*)buffer, bufferBytes, 0);
if (SOCKET_ERROR==received && WSAEWOULDBLOCK==WSAGetLastError()) {
handles[0] = evt;
handles[1] = *recvCancelEvt;
ret = WSAWaitForMultipleEvents(2, handles, FALSE, INFINITE, FALSE);
if (WAIT_OBJECT_0 == ret) {
bytesReceived = recv(handle->iSocket, (char*)buffer, bufferBytes, 0);
}
}
WSACloseEvent(evt);
return bytesReceived;
}
客户端代码如果要取消recv,则会在WSASetEvent
上调用recvCancelEvt
。
答案 1 :(得分:1)
虽然基于Select或阻塞套接字的解决方案是正确的方法,但是你以100%运行一个核心的原因是由于Sleep的行为: -
查看WinAPI sleep()的文档:
此函数使线程放弃剩余的时间 基于值的间隔切片并变得不可用 dwMilliseconds。系统时钟以恒定速率“滴答”。的 如果 dwMilliseconds小于系统时钟的分辨率 线程可能会睡眠时间少于指定的时间长度。
因此,如果您正在进行轮询,则需要使用更长的睡眠时间(可能是20M,通常比Windows滴答速率稍高),或者使用更准确的多媒体计时器。
答案 2 :(得分:0)
大多数情况下,您对recvfrom的调用都不会返回数据。睡1毫秒并不多。您应该考虑增加睡眠时间(便宜但不是最佳解决方案),或者更好的解决方案,考虑使用事件驱动方法。使用select()或Windows API阻止,直到发出套接字信号或发生您感兴趣的其他事件,然后调用recvfrom。您可能需要为此重新设计程序的主循环。
答案 3 :(得分:0)
我建议使用boost :: asio :: io_service。我们收到大约200MB / s的UDP多播流量,同时最大化现代CPU。这包括完整的可靠性协议和应用程序的数据分派。分析的瓶颈是处理,而不是boost :: asio接收。 Code here