使用 C 绑定到 IOCP 的 UDP 套接字
Binding to a UDP socket for IOCP using C
我正在尝试使用 IOCP 从本地(环回)应用程序的 UDP 端口读取数据。 IOCP 在 TCP/IP 下工作正常,但我无法为 UDP 正确打开套接字。
这就是我正在做的事情:
// same as for tcp/ip
struct sockaddr_in remoteAddr = { 0 };
remoteAddr.sin_addr.s_addr = LOOPBACK_ADDRESS;
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(portNumber);
// using SOCK_DGRAM here
SOCKET sock = INVALID_SOCKET;
sock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_IP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if( sock == INVALID_SOCKET ) {
LOG("WSASocketW failed: %d", WSAGetLastError());
return;
}
nRet = WSAConnect(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr),
NULL, NULL, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
LOG("WSAConnect failed: %d", WSAGetLastError());
return;
}
nRet = WSARecv(sock, &wsabuf, 1, NULL, &flags, &overlapped, NULL);
if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
LOG("WSARecv failed: %d", WSAGetLastError());
return;
}
// no errors of any kind
LOG("seems good so far");
一切都顺利通过,但 GetQueuedCompletionStatus
在工作循环线程中永远不会 returns。如果我做同样的事情来连接到 TCP 套接字(基本上只需将 SOCK_DGRAM
替换为 SOCK_STREAM
),我就会在循环中获取数据。
我做错了什么吗?
(顺便说一句) 我知道我可以使用 WSARecvFrom
,但我想重用 TCP 套接字中尽可能多的代码。 IE。希望设置好所有内容,然后 post WSARecv
在工作线程内调用,而不管套接字的类型 (WSARecv
is supposed to work with UDP properly, AFAIK)。
感谢@WouterHuysentruit 的评论,设法让它工作。
基本上,如果我想使用 WSARecv
接收 UDP 数据包,我需要 bind
。如果我想使用 WSASend
发送 UDP 数据包,我需要 connect
。所以以下工作:
if (port_type == incoming_packets)
{
// bind to port
ret = bind(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr));
...
WSARecv(...);
}
else
{
// this can send to a loopback udp port which is bound in
// a different application
ret = WSAConnect(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr), ...);
...
WSASend(...);
}
正如其他人指出的那样,WSAReceiveFrom
/WSASendTo
通常是 UDP 的更好选择,但在这种情况下,我可以透明地使用 IOCP 支持多种端口类型。
我正在尝试使用 IOCP 从本地(环回)应用程序的 UDP 端口读取数据。 IOCP 在 TCP/IP 下工作正常,但我无法为 UDP 正确打开套接字。
这就是我正在做的事情:
// same as for tcp/ip
struct sockaddr_in remoteAddr = { 0 };
remoteAddr.sin_addr.s_addr = LOOPBACK_ADDRESS;
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(portNumber);
// using SOCK_DGRAM here
SOCKET sock = INVALID_SOCKET;
sock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_IP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if( sock == INVALID_SOCKET ) {
LOG("WSASocketW failed: %d", WSAGetLastError());
return;
}
nRet = WSAConnect(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr),
NULL, NULL, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
LOG("WSAConnect failed: %d", WSAGetLastError());
return;
}
nRet = WSARecv(sock, &wsabuf, 1, NULL, &flags, &overlapped, NULL);
if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
LOG("WSARecv failed: %d", WSAGetLastError());
return;
}
// no errors of any kind
LOG("seems good so far");
一切都顺利通过,但 GetQueuedCompletionStatus
在工作循环线程中永远不会 returns。如果我做同样的事情来连接到 TCP 套接字(基本上只需将 SOCK_DGRAM
替换为 SOCK_STREAM
),我就会在循环中获取数据。
我做错了什么吗?
(顺便说一句) 我知道我可以使用 WSARecvFrom
,但我想重用 TCP 套接字中尽可能多的代码。 IE。希望设置好所有内容,然后 post WSARecv
在工作线程内调用,而不管套接字的类型 (WSARecv
is supposed to work with UDP properly, AFAIK)。
感谢@WouterHuysentruit 的评论,设法让它工作。
基本上,如果我想使用 WSARecv
接收 UDP 数据包,我需要 bind
。如果我想使用 WSASend
发送 UDP 数据包,我需要 connect
。所以以下工作:
if (port_type == incoming_packets)
{
// bind to port
ret = bind(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr));
...
WSARecv(...);
}
else
{
// this can send to a loopback udp port which is bound in
// a different application
ret = WSAConnect(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr), ...);
...
WSASend(...);
}
正如其他人指出的那样,WSAReceiveFrom
/WSASendTo
通常是 UDP 的更好选择,但在这种情况下,我可以透明地使用 IOCP 支持多种端口类型。