我正在写一个类似Skype的VOIP应用程序,它现在运行得很好,但我遇到了一个非常奇怪的问题。
在一个线程中,我在while(true)循环中每次运行两次winsock-recv()函数,以从套接字获取数据。第一个调用得到2个字节,这些字节将被广播到(短)中,而第二个调用得到消息的其余部分,看起来像:
完整消息:[2字节标题|消息,长度由2字节标题确定]
这些数据包是大约49/秒的四舍五入,这将是大约3000字节/秒的四入。
这些数据包的内容是被转换成波形的音频数据。
使用ioctlsocket()
,我确定在我接收到的每个"消息"(2字节+数据)中套接字上是否有一些数据。如果在线程的while(true) loop
中收到消息后,套接字上就有什么东西,那么消息会被接收,但会被丢弃,以应对抢占的延迟。
这个概念运行得很好,但问题是:
当我的VOIP程序运行时,当我并行下载(例如通过浏览器)文件时,套接字上总是堆积了太多数据,因为在下载时,recv() loop
似乎实际上会减慢速度。除了实际的voip上传/下载之外,这种情况还会发生在每次下载/上传的情况下。
我不知道这种行为是从哪里来的,但当我实际上取消了除了应用程序的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);}
不需要使用ioctlsocket()
来丢弃数据。这将表明您的协议设计中存在错误。假设您使用的是TCP(您没有说),如果您的2字节标头总是准确的,那么就不应该有任何遗留数据。在读取2字节的标头,然后读取指定数量的字节后,您在此之后接收到的下一个字节将构成您的下一条消息,不应仅仅因为它的存在而丢弃它。
ioctlsocket()
报告的可用字节数更多,这意味着您接收消息的速度比从套接字读取消息的速度快。让你的阅读代码运行得更快,不要因为速度慢而丢弃好的数据。
你的阅读模式效率不高。与其先读取2个字节,然后读取X个字节,再读取2个比特,依此类推,不如使用更大的缓冲区一次从套接字读取更多的原始数据(使用ioctlsocket()
了解可用字节数,然后一次读取至少该字节数,并将其附加到缓冲区的末尾),然后解析缓冲区中尽可能多的完整消息,然后再次从套接字读取更多的原始数据。一次可以读取的数据越多,接收数据的速度就越快。
为了进一步加快代码的速度,也不要直接处理循环中的消息。改为在另一个线程中进行处理。让reading循环将完整的消息放入队列并返回到reading,然后每当消息可用于处理时,让处理线程从队列中拉出。