当没有可用的目的地时,UDP 套接字的 WSASend 触发 FD_READ

Posted

技术标签:

【中文标题】当没有可用的目的地时,UDP 套接字的 WSASend 触发 FD_READ【英文标题】:WSASend for UDP socket triggers FD_READ when no destination available 【发布时间】:2013-02-04 06:20:40 【问题描述】:

我正在为 UDP 套接字类编写 C++ 代码来处理基本操作(例如连接、发送和接收数据)。我尝试将网络事件机制与 WSAEventSelect 一起用于与套接字关联的这些基本操作。 当我使用 WSASend 将数据发送到接收数据的(UDP)目的地时,一切顺利。 但是,当我使用 WSASend 将数据发送到不存在 (UDP) 或无法通过网络访问的目的地时,我触发了 FD_READ 事件。这当然会导致严重的问题,因为没有实际数据可以接收!

我无法解释为什么会发生这种情况 - 有什么想法吗? 也许我做错了什么,这是我的代码的相关部分:

WSADATA m_wsaData ;         
SOCKET  m_Socket ;          
WSAEVENT    m_SocketEvent ;

if(WSAStartup(MAKEWORD(2,2), &m_wsaData) != 0)

// some error


// Create a new socket to receive datagrams on
struct addrinfo hints, *res = NULL ;
int rc ;
memset(&hints, 0, sizeof(hints)) ;
hints.ai_family = AF_UNSPEC ;
hints.ai_socktype = SOCK_DGRAM ;
hints.ai_protocol = IPPROTO_UDP ;

rc = getaddrinfo("SomePC", "3030", &hints, &res) ;

if(rc == WSANO_DATA)

    // some error


if ((m_Socket = WSASocket(res->ai_family, res->ai_socktype, res->ai_protocol, NULL, 0, 0)) == INVALID_SOCKET)

    // some error
 

// create event and associate it with the socket
m_SocketEvent = WSACreateEvent() ;
if(m_SocketEvent == WSA_INVALID_EVENT)

    // some error

// associate only the following events: close, read, write
if(SOCKET_ERROR == WSAEventSelect(m_Socket, m_SocketEvent, FD_CLOSE+FD_READ+FD_WRITE))

    // some error


// connect to a server
int ConnectRet = WSAConnect(m_Socket, (SOCKADDR*)res->ai_addr, res->ai_addrlen, NULL, NULL, NULL, NULL) ;
if(ConnectRet == SOCKET_ERROR)

    // some error

然后,每当我尝试通过套接字将一些数据发送到未侦听或无法访问的(UDP 套接字)目的地时,我总是会触发 FD_READ:

char buf[32] ;  // some data to send...
WSABUF DataBuf;
DataBuf.len = 32;
DataBuf.buf = (char*)&buf;

DWORD NumBytesActualSent ;

if( SOCKET_ERROR == WSASend(m_Socket, &DataBuf, 1, &NumBytesActualSent,0,0,0))

    if(WSAGetLastError() == WSAEWOULDBLOCK) // non-blocking socket - wait for send ok ?
    
        // handle WSAEWOULDBLOCK...
    
    else
    
        // some error
        return ;
    


int ret = WSAWaitForMultipleEvents(1, &m_SocketEvent, FALSE, INFINITE, FALSE) ;


if(ret == WAIT_OBJECT_0)

    WSANETWORKEVENTS NetworkEvents ;
    ZeroMemory(&NetworkEvents, sizeof(NetworkEvents)) ;
    if(SOCKET_ERROR == WSAEnumNetworkEvents(m_Socket, m_SocketEvent, &NetworkEvents))
    
        return ; // some error
    
    if(NetworkEvents.lNetworkEvents & FD_READ)  // Read ?
    
        if(NetworkEvents.iErrorCode[FD_READ_BIT] != 0)  // read not ok ?
        
            // some error
        
        else
        
            TRACE("Read Event Triggered ! - Why ? ? ? ? ? ? ?\n") ;
        
    

任何帮助或见解将是最appriciated! 谢谢, 阿米特 C.

【问题讨论】:

也许这就是您收到的 ICMP 消息(即目标无法到达)?检查标题,这是一个开始的地方。 检查标题是什么意思(怎么做)?我尝试在这种情况下调用 WSARecv 来读取数据,但收到错误 10054(“...连接已关​​闭...”),使用带有 FIONREAD 的 ioctlsocket 我得到 1 - 意味着我有 1 个字节可用于套接字接收 - 但又一次WSARecv 失败。 【参考方案1】:

检查实际情况的最简单方法是使用Wireshark 捕获数据包。我附近没有 Windows PC 可以为您提供完整的示例,但我猜这是正常行为 - 您尝试发送 UDP 数据报,它被路由器丢弃(找不到主机)或被服务器拒绝(套接字关闭);发回 ICMP 消息以通知您有关失败的信息,这是您收到并获取事件的原因。由于没有实际的用户数据,底层堆栈会转换 ICMP 消息并通过 WSARecv() 返回码为您提供适当的错误消息。 “假”FD_READ 事件是必要的,因为 UDP 是无连接协议,并且没有其他方法可以通知网络状态。只需为 WSARecv() 添加错误处理,您的代码应该可以正常工作。

【讨论】:

感谢您的回复,我使用了 Wireshark 并发现在我描述的情况下,有一条 ICMP 消息被传递到我的应用程序。 不过,UDP 套接字接收“假”FD_READ(以及 ioctlsocket 可用的 1 字节数据)只是为了让它知道连接已关闭,这对我来说似乎有点奇怪。它也使事情变得复杂,因为我正在尝试创建一个用于处理 UDP 通信(初始化、发送、接收等)的类,并且该类的用户将获得一个 read-ready 事件,然后尝试读取数据失败;或者当其中两个套接字通信时,一个正在关闭,但另一个仍在发送,一段时间后,一个新的套接字与发送数据的一个通信 - 将出现此错误。 如果是这样,我想这是面向消息的 socekts 的一些缺点......再次感谢您的回复 - 它有帮助! 很高兴我能以某种方式提供帮助。 Socket 编程是一个不同的世界,有许多独特的方面和自己的规则,所以期待更多奇怪的事情被发现。

以上是关于当没有可用的目的地时,UDP 套接字的 WSASend 触发 FD_READ的主要内容,如果未能解决你的问题,请参考以下文章

UDP的connect

UDP和套接字,recvfrom()返回-1,资源暂时不可用

socket(套接字)实现udp通信

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

如何使用 unix Sockets API 查找本地可用的 UDP 端口

Linux UDP 接收缓冲区的最大大小