我开发了MPEG-ts Streamer。它从文件中读取数据包,并以正确的速度将它们发送给接收方。
现在一切正常,不包括我经常有一些滞后。我在我的代码中搜索了每个可能的错误。我已经优化了我的程序性能。
现在我保留一个日志,其中包含sendto()
函数发送数据包的时间,我还记录了数据包发送时间和发送时间之间的差异。
我注意到,每次数据包比平均值晚很多时,sendto()
发送前一个数据包的时间也远高于正常数据包。
这表明,每次以某种方式发送数据包需要更长时间时,sendto()
都会导致这些滞后。我正在使用UDP套接字。
我可能在插座上做错了吗?套接字缓冲区是否可能已满并且实际上需要更长时间才能发送数据包?或者我错过了什么?有没有办法在发送之前加速套接字或使其不完全填充缓冲区?
由于这是用于流式传输视频,因此我非常依赖于性能,主要用于HD,因为数据包的数量要高得多,所以会出现滞后现象。
答案 0 :(得分:6)
sendto()
阻止只有两个原因:
检查传出缓冲区的大小:
int buff_size;
int len = sizeof(buff_size);
err = getsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&size,&len);
某些Linux系统默认使用非常小的发送缓冲区(只有几千字节),因此您可能需要将其设置为更大的值。
如果缓冲区较大且sendto()
无论如何都会短暂阻塞(确切地说多长时间?)那么这可能只是开展业务的成本。即使在局域网上,当然也在广域网上,延迟也会发生很大变化。
<强>更新强>
增加UDP缓冲区大小的系统限制:
sysctl -w net.core.wmem_max=1048576
sysctl -w net.core.rmem_max=1048576
您可以通过向/etc/sysctl.conf
添加以下行来使其永久化:
net.core.wmem_max=1048576
net.core.rmem_max=1048576
要在您的应用程序中利用此功能,您需要使用setsockopt()
:
int len, trysize, gotsize;
len = sizeof(int);
trysize = 1048576+32768;
do {
trysize -= 32768;
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&trysize,len);
err = getsockopt(s,SOL_SOCKET,SO_SNDBUF,(char*)&gotsize,&len);
if (err < 0) { perror("getsockopt"); break; }
} while (gotsize < trysize);
printf("Size set to %d\n",gotsize);
对SO_RCVBUF
重复同样的事情。循环很重要,因为许多系统默默地强制执行最大值,该最大值小于您使用sysctl
设置的最大值,并且当setsockopt()
失败时,它会保持先前的值不变。所以你必须尝试许多不同的值,直到你得到一个坚持。同样重要的是不测试(gotsize == trysize)
,因为在某些系统上,设置的结果实际上与您请求的结果不同。
答案 1 :(得分:1)
没有代码就不可能说,但这里有一些提示:
您可以尝试“更好”您的流程。如果这样可以提高性能,那就是OS调度程序问题。
答案 2 :(得分:1)
与TCP不同,UDP不会缓冲数据。源代码最终有助于更好地理解事物。你发送的数据包有多大?在UDP中,最好将有效负载保持在MTU(1500字节)的大小
答案 3 :(得分:0)
[编辑] 用有缓冲区的东西替换接收器。或者也许尝试发送比接收器真正需要的更多的数据 - 它确实应该有一个缓冲区。我怀疑世界上是否有人建立了机顶盒和谁也不知道网络滞后和谁没有破产已经
根据您的解释,我怀疑原因在您的代码中。导致速度变慢的原因有很多:
结论:你的问题是接收器没有缓冲区。导致UDP数据包发送延迟的原因非常多,并且所有这些原因都不在您的代码的控制之下。
原始答案
UDP不是可靠的协议。无法保证数据包及时到达接收方。 UDP的开销比TCP小,但这是以可靠性为代价的。
通常的解决方案是发送超出客户需求的数据。因此,例如,您以全油门发送前10秒。这允许客户端缓存一些数据以延迟播放。
您还应该更改客户端,以便每隔几秒钟将缓存流的长度发送回服务器。然后,服务器可以计算全速发送的数据包数量,以保持客户端缓存的填充。