0

我正在编写一个像 Skype 这样的小 VOIP 应用程序,它现在工作得很好,但我遇到了一个非常奇怪的问题。

在一个线程中,我在 while(true) 循环内调用 winsock recv() 函数两次,每次运行以从套接字获取数据。第一次调用获得 2 个字节,将被转换为(短),而第二次调用获得消息的其余部分,如下所示:

完整消息:[2 字节标题 | 消息,长度由 2Byte Header 决定]

这些数据包大约是 49/秒,大约是 3000 字节/秒。

这些数据包的内容是转换成波形的音频数据。

随着ioctlsocket()我确定在我收到的每个“消息”(2字节+数据)中是否有一些数据在套接字上。如果在我收到while(true) loop线程内的消息后套接字上有东西,则将收到该消息,但会丢弃该消息以防止上行延迟。

这个概念非常有效,但问题是:

当我的 VOIP 程序正在运行并且当我并行下载(例如通过浏览器)一个文件时,总是有太多的数据堆积在套接字上,因为在下载时,recv() loop似乎实际上变慢了。除了实际的 voip up/download 之外,这在每种下载/上传情况下都会发生。

我不知道这种行为来自哪里,但是当我实际上取消除了我的应用程序的 voip 流量之外的所有上传/下载时,我的应用程序再次完美运行。

如果程序完美运行,该ioctlsocket()函数会将 0 写入 bytesLeft 变量,该变量在接收函数所在的类中定义。

有人知道这是从哪里来的吗?我将在下面附上我的接收功能:

std::string D_SOCKETS::receive_message(){

recv(ClientSocket,(char*)&val,sizeof(val),MSG_WAITALL);
receivedBytes = recv(ClientSocket,buffer,val,MSG_WAITALL);

if (receivedBytes != val){

    printf("SHORT: %d PAKET: %d ERROR: %d",val,receivedBytes,WSAGetLastError());
    exit(128);
}


ioctlsocket(ClientSocket,FIONREAD,&bytesLeft);
cout<<"Bytes left on the Socket:"<<bytesLeft<<endl;

if(bytesLeft>20)
{
    // message gets received, but ignored/thrown away to throw away
    return std::string();
}
else
    return std::string(buffer,receivedBytes);}
4

1 回答 1

1

没有必要使用ioctlsocket()丢弃数据。这表明您的协议设计中存在错误。假设您使用的是 TCP(您没有说),如果您的 2 字节标头始终准确,则不应有任何剩余数据。在读取 2byte 标头,然后读取指定的字节数之后,您收到的下一个字节构成您的下一条消息,不应仅仅因为它存在而被丢弃。

报告更多可用字节的事实ioctlsocket()意味着您接收消息的速度比从套接字读取消息的速度要快。让你的阅读代码跑得更快,不要因为你的慢而丢掉好的数据。

你的阅读模式效率不高。而不是读取 2 个字节,然后是 X 个字节,然后是 2 个字节,等等,您应该使用更大的缓冲区一次从套接字读取更多的原始数据(用于ioctlsocket()知道有多少字节可用,然后读取一次至少有那么多字节并将它们附加到缓冲区的末尾),然后解析缓冲区中尽可能多的完整消息,然后再次从套接字读取更多原始数据。一次可以读取的数据越多,接收数据的速度就越快。

为了进一步加快代码速度,也不要直接处理循环内的消息。而是在另一个线程中进行处理。让阅读循环将完整的消息放入队列并返回阅读,然后在消息可用于处理时从队列中拉出处理线程。

于 2012-02-21T16:57:41.117 回答