Partager via


Notification de congestion explicite winsock (ECN)

Introduction

Certaines applications et/ou protocoles basés sur le protocole UDP (User Datagram Protocol) (par exemple, QUIC) cherchent à tirer parti de l’utilisation de points de code ECN (notification de congestion explicite) afin d’améliorer la latence et la gigue dans les réseaux congestionnés.

Les API ECN Winsock étendent l’interface getsockopt/setsockopt , ainsi que l’interface de message de contrôle WSASendMsg/LPFN_WSARECVMSG (WSARecvMsg) avec prise en charge de la modification et de la réception des points de code ECN dans les en-têtes IP. La fonctionnalité fournie vous permet d’obtenir et de définir des points de code ECN par paquet.

Pour plus d’informations sur ecn, consultez L’ajout d’une notification de congestion explicite (ECN) à l’adresse IP.

Votre application n’est pas autorisée à spécifier le point de code Congestion rencontrée (CE) lors de l’envoi de datagrammes. L’envoi retourne avec l’erreur WSAEINVAL.

Interroger ECN avec WSAGetRecvIPEcn

WSAGetRecvIPEcn est une fonction inline, définie dans ws2tcpip.h.

Appelez WSAGetRecvIPEcn pour interroger l’activation actuelle de la réception du message de contrôle IP_ECN (ou IPV6_ECN) via LPFN_WSARECVMSG (WSARecvMsg).

Consultez également la structure WSAMSG .

  • Protocole : IPv4

  • Cmsg_level : IPPROTO_IP

  • Cmsg_type : IP_ECN (50 décimales)

  • Description : spécifie/reçoit le point de code ECN dans le champ d’en-tête IPv4 type de service (TOS).

  • Protocole : IPv6

  • Cmsg_level : IPPROTO_IPV6

  • Cmsg_type : IPV6_ECN (50 décimales)

  • Description : spécifie/reçoit le point de code ECN dans le champ d’en-tête IPv6 de la classe de trafic.

Spécifier ECN avec WSASetRecvIPEcn

WSASetRecvIPEcn est une fonction inline, définie dans ws2tcpip.h.

Appelez WSASetRecvIPEcn pour spécifier si la pile IP doit remplir la mémoire tampon de contrôle avec un message contenant le codepoint ECN du champ d’en-tête Type de service IPv4 (ou du champ d’en-tête IPv6 de la classe de trafic) sur un datagramme reçu. Lorsqu’elle TRUEest définie sur , la fonction LPFN_WSARECVMSG (WSARecvMsg) retourne des données de contrôle facultatives contenant le point de code ECN du datagramme reçu. Le type de message de contrôle retourné sera IP_ECN (ou IPV6_ECN) avec IPPROTO_IP de niveau (ou IPPROTO_IPV6). Les données de message de contrôle sont retournées en tant qu’INT. Cette option est valide uniquement sur les sockets de datagramme (le type de socket doit être SOCK_DGRAM).

Exemple de code 1 : prise en charge ecn de publicité d’application

#define ECN_ECT_0 2

void sendEcn(SOCKET sock, PSOCKADDR_STORAGE addr, LPFN_WSASENDMSG sendmsg, PCHAR data, INT datalen)
{
    DWORD numBytes;
    INT error;

    CHAR control[WSA_CMSG_SPACE(sizeof(INT))] = { 0 };
    WSABUF dataBuf;
    WSABUF controlBuf;
    WSAMSG wsaMsg;
    PCMSGHDR cmsg;

    dataBuf.buf = data;
    dataBuf.len = datalen;
    controlBuf.buf = control;
    controlBuf.len = sizeof(control);
    wsaMsg.name = (PSOCKADDR)addr;
    wsaMsg.namelen = (INT)INET_SOCKADDR_LENGTH(addr->ss_family);
    wsaMsg.lpBuffers = &dataBuf;
    wsaMsg.dwBufferCount = 1;
    wsaMsg.Control = controlBuf;
    wsaMsg.dwFlags = 0;

    cmsg = WSA_CMSG_FIRSTHDR(&wsaMsg);
    cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(INT));
    cmsg->cmsg_level = (addr->ss_family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;
    cmsg->cmsg_type = (addr->ss_family == AF_INET) ? IP_ECN : IPV6_ECN;
    *(PINT)WSA_CMSG_DATA(cmsg) = ECN_ECT_0;

    error =
        sendmsg(
            sock,
            &wsaMsg,
            0,
            &numBytes,
            NULL,
            NULL);
    if (error == SOCKET_ERROR) {
        printf("sendmsg failed %d\n", WSAGetLastError());
    }
}

Exemple de code 2 : application détectant la congestion

#define ECN_ECT_CE 3

int recvEcn(SOCKET sock, PSOCKADDR_STORAGE addr, LPFN_WSARECVMSG recvmsg, PCHAR data, INT datalen, PBOOLEAN congestionEncountered)
{
    DWORD numBytes;
    INT error;
    INT ecnVal;
    SOCKADDR_STORAGE remoteAddr = { 0 };

    CHAR control[WSA_CMSG_SPACE(sizeof(INT))] = { 0 };
    WSABUF dataBuf;
    WSABUF controlBuf;
    WSAMSG wsaMsg;
    PCMSGHDR cmsg;

    dataBuf.buf = data;
    dataBuf.len = datalen;
    controlBuf.buf = control;
    controlBuf.len = sizeof(control);
    wsaMsg.name = (PSOCKADDR)&remoteAddr;
    wsaMsg.namelen = sizeof(remoteAddr);
    wsaMsg.lpBuffers = &dataBuf;
    wsaMsg.dwBufferCount = 1;
    wsaMsg.Control = controlBuf;
    wsaMsg.dwFlags = 0;

    *congestionEncountered = FALSE;

    error =
        recvmsg(
            sock,
            &wsaMsg,
            &numBytes,
            NULL,
            NULL);
    if (error == SOCKET_ERROR) {
        printf("recvmsg failed %d\n", WSAGetLastError());
        return -1;
    }

    cmsg = WSA_CMSG_FIRSTHDR(&wsaMsg);
    while (cmsg != NULL) {
        if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_ECN) ||
            (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_ECN)) {
            ecnVal = *(PINT)WSA_CMSG_DATA(cmsg);
            if (ecnVal == ECN_ECT_CE) {
                *congestionEncountered = TRUE;
            }
            break;
        }
        cmsg = WSA_CMSG_NXTHDR(&wsaMsg, cmsg);
    }

    return numBytes;
}

void receiver(SOCKET sock, PSOCKADDR_STORAGE addr, LPFN_WSARECVMSG recvmsg)
{
    DWORD numBytes;
    INT error;
    DWORD enabled;
    CHAR data[512];
    BOOLEAN congestionEncountered;

    error = bind(sock, (PSOCKADDR)addr, sizeof(*addr));
    if (error == SOCKET_ERROR) {
        printf("bind failed %d\n", WSAGetLastError());
        return;
    }

    enabled = TRUE;
    error = WSASetRecvIPEcn(sock, enabled);
    if (error == SOCKET_ERROR) {
        printf(" WSASetRecvIPEcn failed %d\n", WSAGetLastError());
        return;
    }

    do {
        numBytes = recvEcn(sock, addr, recvmsg, data, sizeof(data), &congestionEncountered);
        if (congestionEncountered) {
            // Tell sender to slow down
        }
    } while (numBytes > 0);
}

Voir aussi