Подтвердить что ты не робот

Нужно ли связывать сокет UDP в моей клиентской программе для получения данных? (Я всегда получаю WSAEINVAL)

Я создаю UDP-сокет (AF_INET, SOCK_DGRAM, IPPROTO_UDP) через Winsock и пытаюсь recvfrom в этом сокете, но он всегда возвращает -1, и я получаю WSAEINVAL (10022). Почему?

Когда я bind() порт, этого не происходит, но я прочитал, что очень сложно связать клиентский сокет.

Я отправляю данные на свой сервер, который отвечает или, по крайней мере, пытается.

Inc::STATS CConnection::_RecvData(sockaddr* addr, std::string &strData)
{
    int ret;            // return code
    int len;            // length of the data
    int fromlen;        // sizeof(sockaddr)
    char *buffer;       // will hold the data
    char c;

    //recv length of the message
    fromlen = sizeof(sockaddr);
    ret = recvfrom(m_InSock, &c, 1, 0, addr, &fromlen);
    if(ret != 1)
    {
#ifdef __MYDEBUG__
        std::stringstream ss;
        ss << WSAGetLastError();
        MessageBox(NULL, ss.str().c_str(), "", MB_ICONERROR | MB_OK);
#endif
        return Inc::ERECV;
    }
    ...

Это рабочий пример, который я написал несколько минут назад, и он работает без вызова bind() в клиенте:

#pragma comment(lib, "Ws2_32.lib")

#define WIN32_LEAN_AND_MEAN

#include <WS2tcpip.h>
#include <Windows.h>
#include <iostream>

using namespace std;

int main()
{
    SOCKET sock;
    addrinfo* pAddr;
    addrinfo hints;
    sockaddr sAddr;
    int fromlen;
    const char czPort[] = "12345";
    const char czAddy[] = "some ip";

    WSADATA wsa;
    unsigned short usWSAVersion = MAKEWORD(2,2);

    char Buffer[22] = "TESTTESTTESTTESTTEST5";
    int ret;

    //Start WSA
    WSAStartup(usWSAVersion, &wsa);

    //Create Socket
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    //Resolve host address
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_socktype = SOCK_DGRAM;

    if(getaddrinfo(czAddy, czPort, &hints, &pAddr))
    {
        std::cerr << "Could not resolve address...\n";
        std::cin.get();
        return 1;
    }

    //Start Transmission
    while(1)
    {
        ret = sendto(sock, Buffer, sizeof(Buffer), 0, pAddr->ai_addr,
                     pAddr->ai_addrlen);
        if(ret != sizeof(Buffer))
        {
            std::cerr << "Could not send data\n";
            std::cin.get();
            return 1;
        }

        fromlen = sizeof(SOCKADDR);
        ret = recvfrom(sock, Buffer, sizeof(Buffer), 0, &sAddr, &fromlen);
        if(ret != sizeof(Buffer))
        {
            std::cout << "Could not receive data -  error: " << 
                          WSAGetLastError() << std::endl;
            std::cin.get();
            return 1;
        }

        Buffer[ret-1] = '\0';
        std::cout << "Received: " << Buffer << std::endl;
    }
    return 0;
}
4b9b3361

Ответ 1

С UDP вы должны bind() сокет в клиенте, потому что UDP является без установления соединения, поэтому нет другого способа для стека узнать, какая программа для доставки датаграмм для определенного порта.

Если вы могли recvfrom() без bind(), вы, по сути, попросили бы стек предоставить вашей программе все датаграммы UDP, отправленные на этот компьютер. Поскольку стек предоставляет дейтаграммы только для одной программы, это может привести к поломке DNS, "Сетевому соседству" Windows, сетевой синхронизации времени....

Возможно, вы читали где-то в сети, что привязка к клиенту является хромой, но этот совет применяется только к соединениям TCP.

Ответ 2

Ваш другой пример кода работает, потому что вы используете sendto до recvfrom. Если сокет UDP не подключен и на него вызывается sendto или connect, система автоматически привяжет его для вас, и, следовательно, вызов recvfrom позже будет успешным. recvfrom не будет связывать сокет, так как этот вызов ожидает, что сокет уже был связан, или будет сброшена ошибка.

Ответ 3

У меня была та же проблема несколько недель назад, следующие замечания помогли мне понять, нужен ли явный вызов bind():

функция recvfrom (MSDN)

Явное связывание не рекомендуется для клиентских приложений. Для клиентских приложений, использующих эту функцию, сокет может привязываться неявно к локальному адресу через sendto, WSASendTo или WSAJoinLeaf.

функция sendto (MSDN)

Примечание. Если сокет открыт, выполняется вызов setsockopt, а затем выполняется вызов sendto, Windows Sockets выполняет неявное привязку. > вызов функции. Если сокет не подключен, уникальные значения присваиваются локальной ассоциации системой, а затем сокет помечен как связанный.

Ответ 4

Здесь сказано следующее:

Параметры

s [in]: дескриптор, идентифицирующий связанный сокет.

...

Возвращаемое значение

WSAEINVAL: сокет не связан с привязкой или не указан неизвестный флаг, или MSG_OOB был указан для сокета с включенным SO_OOBINLINE или (только для сокетов потока байтов) len был равен нулю или отрицателен.

Насколько я помню, привязка не требуется для сокета UDP, потому что вызов вызова выполняется для вас стеком. Я предполагаю, что для Windows требуется требование привязки к сокету, используемому при вызове recvfrom.