使用 C 绑定到 IOCP 的 UDP 套接字

Posted

技术标签:

【中文标题】使用 C 绑定到 IOCP 的 UDP 套接字【英文标题】:Binding to a UDP socket for IOCP using C 【发布时间】:2017-04-10 18:51:01 【问题描述】:

我正在尝试使用 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 永远不会返回。如果我做同样的事情来连接到 TCP 套接字(基本上只需将 SOCK_DGRAM 替换为 SOCK_STREAM),我就会在循环中获取数据。

我做错了什么吗?

(顺便说一句) 我知道我可以使用WSARecvFrom,但我想尽可能多地重用来自 TCP 套接字的代码。 IE。希望设置好一切,然后在工作线程中发布WSARecv 调用,无论套接字的类型如何(WSARecv is supposed to work with UDP properly, AFAIK)。

【问题讨论】:

你想重用你的代码还是让它工作? UDP 是无连接的,因此调用WSAConnect 没有多大意义(除非您加入单播/多播组)。 UDP 套接字必须使用bind 绑定到端口/接口。 @Wouter:如果可能的话,重用并让它工作,当然,这就是我写这个问题的原因。 :) 这是否意味着我不能将WSARecvbind 一起使用,只能使用WSARecvFrom?你能发布一个描述这个的答案吗? 我自己从未使用过WSARecv(仅recv()),但我认为您应该能够将WSARecvFrom 替换为bind() + WSARecv 甚至在文档中:The WSARecv function receives data from a connected socket or a bound connectionless socket.bound 一词指的是bind() 函数。 你在哪里绑定到 IOCP ??也对于 UDP WSAConnect 毫无意义,需要使用 WSARecvFrom 而不是 WSARecv 【参考方案1】:

感谢@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 支持多种端口类型。

【讨论】:

以上是关于使用 C 绑定到 IOCP 的 UDP 套接字的主要内容,如果未能解决你的问题,请参考以下文章

在 Ruby 中将 UDP 套接字绑定到通配符主机

udp 套接字发送到隐式绑定

重新绑定 UDP 套接字

在 node.js 中,如何创建一个自动绑定到可用端口的 UDP 套接字?

绑定多播 (UDP) 套接字是啥意思?

UDP 套接字设置超时