-1

我正在实现一个语音聊天服务器,它将在我的 Windows 虚拟课堂电子学习应用程序中使用,它使用远程桌面 API

到目前为止,我一直在使用 OPUS 压缩声音,并且测试了各种选项:

  1. 通过 RDP虚拟通道传递语音。这可行,但尽管使用 CHANNEL_PRIORITY_HI 创建频道,但它会产生很多延迟。
  2. 使用我自己的 TCP(或 UDP)语音服务器。对于这个选项,我一直想知道什么是最好的实现方法。

目前我正在将收到的 udp 数据报发送给所有其他客户端(稍后我将进行服务器端混合)。

我当前的 UDP 语音服务器的问题是即使在同一台电脑中也有延迟:例如,一台服务器和四个客户端连接,其中两个有开放式麦克风。

我在这个设置中听到了延迟:

void VoiceServer(int port)
{
    XSOCKET Y = make_shared<XSOCKET>(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (!Y->Bind(port))
        return;

    auto VoiceServer2 = [&]() 
    {
        OPUSBUFF o;
        char d[200] = { 0 };
        map<int, vector<char>> udps;
        for (;;)
        {
            // get datagram
            int sle = sizeof(sockaddr_in6);
            int r = recvfrom(*Y, o.d, 4000, 0, (sockaddr*)d, &sle);
            if (r <= 0)
                break;

            // a MESSAGE is a header and opus data follows
            MESSAGE* m = (MESSAGE*)o.d;

            // have we received data from this client already?
            // m->arb holds the RDP ID of the user  
            if (udps.find(m->arb) == udps.end())
            {
                vector<char>& uu = udps[m->arb];
                uu.resize(sle);
                memcpy(uu.data(), d, sle);
            }

            for (auto& att2 : aatts) // attendee list
            {
                long lxid = 0;
                att2->get_Id(&lxid);
#ifndef _DEBUG
                if (lxid == m->arb) // if same
                    continue;
#endif
                const vector<char>& uud = udps[lxid];
                sendto(*Y, o.d + sizeof(MESSAGE), r - sizeof(MESSAGE), 0, (sockaddr*)uud.data(), uud.size());
            }
        }
    };

    // 10 threads receiving
    for (int i = 0; i < 9; i++)
    {
        std::thread t(VoiceServer2);
        t.detach();
    }
    VoiceServer2();

}

每个客户端运行一个 VoiceServer 线程:

void VoiceServer()
{
    char b[4000] = { 0 };
    vector<char> d2;
    for (;;)
    {
        int r = recvfrom(Socket, b, 4000, 0, 0,0);
        if (r <= 0)
            break;

        d2.resize(r);
        memcpy(d2.data(), b, r);

        if (audioin && wout)
            audioin->push(d2); // this pushes the buffer to a waveOut writing class
        SetEvent(hPlayEvent);
    }
}

这是因为我在同一台机器上测试吗?但是对于我过去设置的 TeamSpeak 客户端,没有任何延迟。

感谢您的意见。

4

1 回答 1

0

发送到():

对于面向消息的套接字,必须注意不要超过底层子网的最大数据包大小,可以通过使用getsockopt检索套接字选项的值来 获得SO_MAX_MSG_SIZE。如果数据太长而无法原子地通过底层协议,则返回错误 WSAEMSGSIZE 并且不传输任何数据。

一个典型的 IPv4 报头是 20 字节,而 UDP 报头是 8 字节。UDP 数据包最大大小的理论限制(在 Windows 上)为 65507 字节(由以下公式确定:0xffff - 20 - 8 = 65507)。发送这么大的数据包实际上是最好的方法吗?如果我们将数据包大小设置得太大,网络协议底层将在 IP 层拆分数据包。这会占用大量的网络带宽,造成延迟。

MTU(最大传输单元),实际上和链路层协议有关。EthernetII帧的结构DMAC+SMAC+Type+Data+CRC由于以太网传输的电气限制,每个以太网帧的最小尺寸为64字节,最大尺寸不能超过1518字节。对于小于或大于此限制的以太网帧,我们可以将其视为错误。由于以太网EthernetII的最大数据帧为1518字节,除帧头14Bytes和帧尾CRC校验部分4Bytes外,数据域仅剩1500字节。那是MTU。

在 MTU 为 1500 字节的情况下,如果希望 IP 层不拆分数据包,则 UDP 数据包的最大大小应为 1500 字节 - IP 头(20 字节) - UDP 头(8 字节) = 1472 字节。但是,由于 Internet 上的标准 MTU 值是 576 字节,因此在 Internet 上编程 UDP 时,建议将 UDP 数据长度控制在 sendto/recvfrom 中的 (576-8-20) 548 字节内。

您需要减少发送/接收的字节数,然后控制次数。

于 2019-02-28T09:20:38.993 回答