Compartir a través de


Notificación de congestión explícita de Winsock (ECN)

Introducción

Algunas aplicaciones o protocolos que se basan en el Protocolo de datagramas de usuario (UDP) (por ejemplo, QUIC) buscan aprovechar el uso de puntos de código de notificación de congestión explícitas (ECN) para mejorar la latencia y la vibración en las redes congested.

Las API ECN de Winsock amplían la interfaz setsockopt de getockopt/, así como la interfaz de mensaje de control WSASendMsg/LPFN_WSARECVMSG (WSARecvMsg), con compatibilidad para modificar y recibir puntos de código ECN en encabezados IP. La funcionalidad proporcionada permite obtener y establecer puntos de código ECN por paquete.

Para obtener más información sobre ECN, vea La adición de notificación de congestión explícita (ECN) a ip.

La aplicación no puede especificar el punto de código congestión encontrado (CE) al enviar datagramas. El envío devolverá el error WSAEINVAL.

Consulta de ECN con WSAGetRecvIPEcn

WSAGetRecvIPEcn es una función insertada, definida en ws2tcpip.h.

Llame a WSAGetRecvIPEcn para consultar la habilitación actual de recibir el mensaje de control IP_ECN (o IPV6_ECN) a través de LPFN_WSARECVMSG (WSARecvMsg).

Consulte también la estructura WSAMSG .

  • Protocolo: IPv4

  • Cmsg_level: IPPROTO_IP

  • Cmsg_type: IP_ECN (50 decimales)

  • Descripción: especifica o recibe el punto de código ECN en el campo de encabezado IPv4 tipo de servicio (TOS).

  • Protocolo: IPv6

  • Cmsg_level: IPPROTO_IPV6

  • Cmsg_type: IPV6_ECN (50 decimales)

  • Descripción: especifica o recibe el punto de código ECN en el campo de encabezado IPv6 de clase de tráfico.

Especificar ECN con WSASetRecvIPEcn

WSASetRecvIPEcn es una función insertada, definida en ws2tcpip.h.

Llame a WSASetRecvIPEcn para especificar si la pila ip debe rellenar el búfer de control con un mensaje que contenga el punto de código ECN del campo de encabezado IPv4 de tipo de servicio (o campo de encabezado IPv6 de clase de tráfico) en un datagrama recibido. Cuando se establece TRUEen , la función LPFN_WSARECVMSG (WSARecvMsg) devuelve datos de control opcionales que contienen el punto de código ECN del datagrama recibido. El tipo de mensaje de control devuelto se IP_ECN (o IPV6_ECN) con IPPROTO_IP de nivel (o IPPROTO_IPV6). Los datos del mensaje de control se devuelven como un INT. Esta opción solo es válida en sockets de datagramas (el tipo de socket debe ser SOCK_DGRAM).

Ejemplo de código 1: compatibilidad con ECN de publicidad de aplicaciones

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

Ejemplo de código 2: detección de la congestión de la aplicación

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

Vea también