Condividi tramite


Notifica esplicita della congestione Winsock (ECN)

Introduzione

Alcune applicazioni e/o protocolli basati sul protocollo UDP (User Datagram Protocol) (ad esempio, QUIC) cercano di sfruttare l'uso di punti di codice ECN (Explicit Congestion Notification) per migliorare la latenza e jitter nelle reti congestionate.

Le API Winsock ECN estendono l'interfaccia del messaggio di controllogetsockopt setockopt/, nonché l'interfaccia del messaggio di controlloWSASendMs LPFN_WSARECVMSG g (WSARecvMsg), con supporto per la modifica e la ricezione di punti di codice ECN nelle intestazioni IP./ La funzionalità fornita consente di ottenere e impostare i punti di codice ECN in base a pacchetti.

Per altre informazioni su ECN, vedere L'aggiunta della notifica di congestione esplicita (ECN) all'IP.

L'applicazione non è autorizzata a specificare il punto di codice Di congestione rilevato (CE) durante l'invio di datagrammi. L'invio restituirà con errore WSAEINVAL.

Eseguire query su ECN con WSAGetRecvIPEcn

WSAGetRecvIPEcn è una funzione inline definita in ws2tcpip.h.

Chiamare WSAGetRecvIPEcn per eseguire query sull'abilitazione corrente della ricezione del messaggio di controllo IP_ECN (o IPV6_ECN) tramite LPFN_WSARECVMSG (WSARecvMsg).

Vedere anche la struttura WSAMSG .

  • Protocollo: IPv4

  • Cmsg_level: IPPROTO_IP

  • Cmsg_type: IP_ECN (50 decimali)

  • Descrizione: specifica/riceve il punto di codice ECN nel campo Intestazione IPv4 (Type of Service) IPv4.

  • Protocollo: IPv6

  • Cmsg_level: IPPROTO_IPV6

  • Cmsg_type: IPV6_ECN (50 decimali)

  • Descrizione: specifica/riceve il punto di codice ECN nel campo intestazione IPv6 della classe di traffico.

Specificare ECN con WSASetRecvIPEcn

WSASetRecvIPEcn è una funzione inline definita in ws2tcpip.h.

Chiamare WSASetRecvIPEcn per specificare se lo stack IP deve popolare il buffer di controllo con un messaggio contenente il punto di codice ECN del campo di intestazione IPv4 del servizio (o del campo intestazione IPv6 della classe di traffico) in un datagram ricevuto. Se impostato su TRUE, la funzione LPFN_WSARECVMSG (WSARecvMsg) restituisce dati di controllo facoltativi contenenti il punto di codice ECN del datagram ricevuto. Il tipo di messaggio di controllo restituito sarà IP_ECN (o IPV6_ECN) con IPPROTO_IP di livello (o IPPROTO_IPV6). I dati del messaggio di controllo vengono restituiti come INT. Questa opzione è valida solo nei socket datagram (il tipo di socket deve essere SOCK_DGRAM).

Esempio di codice 1: supporto per la pubblicità delle applicazioni ECN

#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());
    }
}

Esempio di codice 2: l'applicazione che rileva la congestione

#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);
}

Vedi anche