在Windows 2012/2016上,几个1G NIC上的单线程负载平衡UDP流量总计约1.1GB

问题描述

简单的单线程应用通过2或3个NIC发送UDP流量,以发送2GB或3GB流量。 在同一台PC上, Windows 10成功发送每个NIC 1GB ,而在Windows Server 2012/2016上仅获得:

    发送到2个NIC时,每个NIC
  • 〜550Mbps。
  • 发送到3个NIC时,每个NIC
  • 〜380Mbps。
  • 发送到4个NIC时,每个NIC
  • 〜270Mbps。

在Windows 10上将SO_SNDBUF设置为1可提供与Windows 2016/2012相同的性能

它看起来像Windows服务器跳过套接字缓冲区(用SO_SNDBUF设置),然后直接将其写入NIC 。Windows10是否将始终写入袜子缓冲区。

代码更新为多线程(每个套接字1个线程),我在2016年也为每个NIC获得1Gbps。

https://www.binarytides.com/udp-socket-programming-in-winsock/中的代码进行相同的更改:

#include<stdio.h>
#include<winsock2.h>
#include<string>
#include <vector>
#include <filesystem>
#pragma comment(lib,"ws2_32.lib") //Winsock Library

#define SERVER "127.0.0.1"  //ip address of udp server
#define BUFLEN 63*1024  //Max length of buffer
#define PORT 8888   //The port on which to listen for incoming data
struct socket_data
{
    SOCKET s;
    struct sockaddr_in si_other;
    std::string ip;
};


int main(int argc,char **argv)
{
    int slen = sizeof(struct sockaddr_in);
    char message[64 * 1024];
    std::vector<socket_data> sd;
    WSADATA wsa;
    if (2 > argc)
    {
        printf("please provide list of destinations IP.\n");
        printf("For example:\n %ls 22.22.22.255 11.11.11.255 12.12.12.255\n",std::experimental::filesystem::path(argv[0]).filename().c_str());
        exit(EXIT_FAILURE);

    }
    for (size_t i = 1; i < argc; i++)
    {
        socket_data tmp;
        tmp.ip = argv[i];
        sd.push_back(tmp);
    }

    for (int i = 0; i < BUFLEN; ++i)
    {
        message[i] = i;
    }
    //Initialise winsock
    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        printf("Failed. Error Code : %d",WSAGetLastError());
        exit(EXIT_FAILURE);
    }
    printf("Initialised.\n");

    //create socket
    for (int i = 0; i < sd.size(); ++i)
    {
        if ((sd[i].s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) == SOCKET_ERROR)
        {
            printf("socket() Failed with error code : %d",WSAGetLastError());
            exit(EXIT_FAILURE);
        }
        //setup address structure
        memset((char *)&sd[i].si_other,sizeof(sd[i].si_other));
        sd[i].si_other.sin_family = AF_INET;
        sd[i].si_other.sin_port = htons(PORT);
        sd[i].si_other.sin_addr.S_un.S_addr = inet_addr(sd[i].ip.c_str());
        int sendbuff = 20*1024*1024;
        setsockopt(sd[i].s,SOL_SOCKET,SO_SNDBUF,(char*)&sendbuff,sizeof(sendbuff));
    }


    //start communication
    while (1)
    {
        for (int i = 0; i < sd.size(); ++i) {
            //send the message
            if (sendto(sd[i].s,message,BUFLEN - 1,(struct sockaddr *) &sd[i].si_other,slen) == SOCKET_ERROR)
            {
                printf("sendto() Failed with error code : %d",WSAGetLastError());
                exit(EXIT_FAILURE);
            }
        }
    }

    for (int i = 0; i < 3; ++i)
    {
        closesocket(sd[i].s);
    }
    WSACleanup();

    return 0;
}

解决方法

在 linux 上没有问题,我使用下面的代码为每个 NIC 获得 1Gbps。 对于 Windows 2016/2012,我设法使用连接的套接字解​​决了这个问题。我使用 ACE_SOCK_CODgram,但我确定使用上面相同的代码与 connect 可以工作(ACE_SOCK_CODgram 连接)。