WSASend для сокета UDP запускает FD_READ, когда пункт назначения недоступен

Я пишу код C++ для класса сокета UDP для обработки основных операций (таких как подключение, отправка и получение данных). Я пытаюсь использовать механизм сетевых событий с 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") ;
        }
    }
}

Любая помощь или идеи будут наиболее признательны! Спасибо, Амит С.


person A-C    schedule 04.02.2013    source источник
comment
Может быть, это сообщение ICMP, которое вы получаете (например, пункт назначения недоступен)? Проверьте заголовок, это место для начала.   -  person KBart    schedule 04.02.2013
comment
Что вы подразумеваете под проверкой заголовка (как это сделать)? Я попытался вызвать WSARecv для чтения данных в этой ситуации, но получил ошибку 10054 (... соединение было закрыто...), используя ioctlsocket с FIONREAD, я получаю 1 - это означает, что я получил 1 байт, доступный для сокета для получения - но снова WSARecv терпит неудачу .   -  person A-C    schedule 04.02.2013


Ответы (1)


Самый простой способ узнать, что происходит на самом деле, — использовать Wireshark для перехвата пакетов. У меня нет поблизости ПК с Windows, чтобы предоставить вам полный пример, но я предполагаю, что это нормальное поведение — вы пытаетесь отправить дейтаграмму UDP, она отбрасывается маршрутизатором (хост не найден) или отклоняется сервером (сокет закрыто); сообщение ICMP отправляется обратно, чтобы проинформировать вас о сбое, для чего вы получаете и получаете событие. Поскольку реальных пользовательских данных нет, базовый стек преобразует сообщение ICMP и предоставляет вам соответствующее сообщение об ошибке с помощью кода возврата WSARecv(). «Ложное» событие FD_READ необходимо, так как UDP является протоколом без установления соединения и нет других средств для информирования о состоянии сети. Просто добавьте обработку ошибок для WSARecv(), и ваш код должен работать нормально.

person KBart    schedule 04.02.2013
comment
Спасибо за ваш ответ, я использовал Wireshark и обнаружил, что сообщение ICMP доставляется в мое приложение в описанной мной ситуации. - person A-C; 05.02.2013
comment
Тем не менее, мне кажется немного странным, что сокет UDP получает ложный FD_READ (и 1 байт данных, доступных с ioctlsocket) только для того, чтобы сообщить ему, что соединение не работает. Это также усложняет ситуацию, поскольку я пытаюсь создать класс для обработки связи UDP (инициализация, отправка, получение и т. д.), и пользователь этого класса получит событие готовности к чтению, а затем не сможет прочитать данные; Или когда два из этих сокетов обмениваются данными, тогда один отключается, а другой все еще отправляет данные, и через некоторое время новый сокет связывается с тем, который отправляет данные - возникнет эта ошибка. - person A-C; 05.02.2013
comment
Если да, то я думаю, что это некоторые из недостатков ориентированных на сообщения сокетов... И еще раз спасибо за ваш ответ - это помогло! - person A-C; 05.02.2013
comment
Рад, что смог чем-то помочь. Программирование сокетов — это другой мир со многими уникальными аспектами и собственными правилами, так что ожидайте открытия более странных вещей. - person KBart; 05.02.2013