ECN) (Winsock 明確壅塞通知
簡介
例如,根據使用者資料包通訊協定 (UDP) (的某些應用程式和/或通訊協定,QUIC) 會嘗試利用明確壅塞通知 (ECN) 程式碼點的使用,以改善叢集網路中延遲和抖動。
Winsock ECN API 會擴充getsockopt setockopt/ 介面,以及WSASendMsg/LPFN_WSARECVMSG (WSARecvMsg) 控制訊息介面,並支援在 IP 標頭中修改和接收 ECN 程式碼點。 所提供的功能可讓您根據每個封包取得和設定 ECN 程式碼點。
如需 ECN 的詳細資訊,請參閱 將明確壅塞通知新增 (ECN) 至 IP。
傳送資料包時,不允許您的應用程式指定壅塞發生 (CE) 字碼點。 傳送會傳回錯誤 WSAEINVAL。
使用 WSAGetRecvIPEcn 查詢 ECN
WSAGetRecvIPEcn 是在 中 ws2tcpip.h
定義的內嵌函式。
呼叫WSAGetRecvIPEcn以透過WSARecvMsg ) 查詢接收IP_ECN (或IPV6_ECN) 控制 LPFN_WSARECVMSG (訊息的目前啟用。
另請參閱 WSAMSG 結構。
通訊協定:IPv4
Cmsg_level:IPPROTO_IP
Cmsg_type:IP_ECN (50 個小數)
描述:指定/接收 [服務類型] (TOS) IPv4 標頭欄位中的 ECN 代碼點。
通訊協定:IPv6
Cmsg_level:IPPROTO_IPV6
Cmsg_type:IPV6_ECN (50 十進位)
描述:指定/接收流量類別 IPv6 標頭欄位中的 ECN 代碼點。
使用 WSASetRecvIPEcn 指定 ECN
WSASetRecvIPEcn 是在 中 ws2tcpip.h
定義的內嵌函式。
呼叫 WSASetRecvIPEcn 以指定 IP 堆疊是否應該在接收的資料包上填入包含類型 IPv4 標頭欄位 (之 ECN 代碼點的訊息,或流量類別 IPv6 標頭欄位) 的訊息。 當設定為 TRUE
時, LPFN_WSARECVMSG (WSARecvMsg) 函式會傳回選擇性的控制資料,其中包含所接收資料包的 ECN 字碼點。 傳回的控制訊息類型將會IP_ECN (或IPV6_ECN) 層級IPPROTO_IP (或IPPROTO_IPV6) 。 控制項訊息資料會以 INT傳回。 此選項只有在通訊端類型必須 SOCK_DGRAM) ,才能在資料包通訊端 (有效。
程式碼範例 1— 應用程式廣告 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());
}
}
程式碼範例 2— 應用程式偵測壅塞
#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);
}