Winsock Explicit Congestion Notification (ECN)

Einführung

Einige Anwendungen und/oder Protokolle, die auf dem User Datagram Protocol (UDP) basieren (z. B. QUIC), versuchen, die Verwendung von ECN-Codepunkten (Explicit Congestion Notification) zu nutzen, um Latenz und Jitter in überlasteten Netzwerken zu verbessern.

Die Winsock ECN-APIs erweitern die getsockopt/setsockopt-Schnittstelle sowie die WSASendMsg-LPFN_WSARECVMSG/ -Steuerelementschnittstelle(WSARecvMsg) um Unterstützung für das Ändern und Empfangen von ECN-Codepunkten in IP-Headern. Mit der bereitgestellten Funktionalität können Sie ECN-Codepunkte pro Paket abrufen und festlegen.

Weitere Informationen zu ECN finden Sie unter Hinzufügen der expliziten Überlastungsbenachrichtigung (ECN) zu IP.

Ihre Anwendung darf beim Senden von Datagrammen nicht den Ce-Codepunkt (Congestion Encountered, Überlastung gefunden) angeben. Der Sendevorgang wird mit dem Fehler WSAEINVAL zurückgegeben.

Abfragen von ECN mit WSAGetRecvIPEcn

WSAGetRecvIPEcn ist eine Inlinefunktion, die in ws2tcpip.hdefiniert ist.

Rufen Sie WSAGetRecvIPEcn auf, um die aktuelle Aktivierung des Empfangens der IP_ECN -Steuerelementnachricht (oder IPV6_ECN) über LPFN_WSARECVMSG (WSARecvMsg) abzufragen.

Siehe auch die WSAMSG-Struktur .

  • Protokoll: IPv4

  • Cmsg_level: IPPROTO_IP

  • Cmsg_type: IP_ECN (50 Dezimalstellen)

  • Beschreibung: Gibt den ECN-Codepunkt im IPv4-Headerfeld Typ des Diensts (TOS) an.

  • Protokoll: IPv6

  • Cmsg_level: IPPROTO_IPV6

  • Cmsg_type: IPV6_ECN (50 Dezimalstellen)

  • Beschreibung: Gibt den ECN-Codepunkt im IPv6-Headerfeld der Datenverkehrsklasse an.

Angeben von ECN mit WSASetRecvIPEcn

WSASetRecvIPEcn ist eine Inlinefunktion, die in ws2tcpip.hdefiniert ist.

Rufen Sie WSASetRecvIPEcn auf, um anzugeben, ob der IP-Stapel den Steuerungspuffer mit einer Nachricht auffüllen soll, die den ECN-Codepunkt des IPv4-Headerfelds "Diensttyp" (oder "Traffic Class IPv6-Headerfeld") in einem empfangenen Datagramm enthält. Bei Festlegung auf TRUEgibt die funktion LPFN_WSARECVMSG (WSARecvMsg) optionale Steuerungsdaten zurück, die den ECN-Codepunkt des empfangenen Datagramms enthalten. Der zurückgegebene Steuerelementmeldungstyp ist IP_ECN (oder IPV6_ECN) mit IPPROTO_IP (oder IPPROTO_IPV6). Die Kontrollmeldungsdaten werden als INT zurückgegeben. Diese Option ist nur für Datagrammsockets gültig (der Sockettyp muss SOCK_DGRAM sein).

Codebeispiel 1– ECN-Unterstützung für Anwendungswerbung

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

Codebeispiel 2: Anwendungserkennung von Überlastungen

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

Siehe auch