繁体   English   中英

我是否必须在客户端程序中绑定 UDP 套接字才能接收数据? (我总是得到 WSAEINVAL)

[英]Do I have to bind a UDP socket in my client program to receive data? (I always get WSAEINVAL)

我正在通过 Winsock 创建一个 UDP 套接字( AF_INETSOCK_DGRAMIPPROTO_UDP )并尝试在此套接字上进行接收,但它总是返回 -1 并且我得到recvfrom (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;
}

使用 UDP,您必须在客户端bind()套接字,因为 UDP 是无连接的,因此堆栈没有其他方法可以知道将数据报传递到特定端口的哪个程序。

如果您可以在没有bind() ) 的情况下使用recvfrom() ) ,那么您实际上是在要求堆栈为您的程序提供所有发送到该计算机的 UDP 数据报。 由于堆栈仅将数据报传递给一个程序,这将破坏 DNS、Windows 的网络邻居、网络时间同步......

您可能在网上某处读到客户端中的绑定是蹩脚的,但该建议仅适用于 TCP 连接。

您的其他代码示例有效,因为您在recvfrom之前使用了sendto 如果 UDP 套接字未绑定,并且在其上调用了sendtoconnect ,系统将自动为您绑定它,因此稍后的recvfrom调用将成功。 recvfrom不会绑定套接字,因为此调用期望套接字已经绑定,否则将引发错误。

几周前我遇到了同样的问题,以下评论帮助我理解是否需要显式调用 bind() :

recvfrom 函数 (MSDN)

不鼓励对客户端应用程序进行显式绑定。 对于使用此函数的客户端应用程序,套接字可以通过sendtoWSASendToWSAJoinLeaf隐式绑定到本地地址。

发送到函数 (MSDN)

注意如果打开套接字,调用 setsockopt,然后调用sendto ,Windows 套接字将执行隐式绑定函数调用。 如果套接字未绑定,系统会为本地关联分配唯一值,然后将套接字标记为已绑定。

这里它说:

参数

s [in]:标识绑定套接字的描述符。

...

返回值

WSAEINVAL:套接字尚未与 bind 绑定,或者指定了未知标志,或者为启用了 SO_OOBINLINE 的套接字指定了 MSG_OOB,或者(仅对于字节流样式的套接字)len 为零或负数。

据我所知,UDP 套接字不需要绑定,因为堆栈为您进行了绑定调用。 我想这是 Windows 的事情,需要在 recvfrom 调用中使用的套接字上进行绑定。

如果您希望您的套接字绑定到系统自动选择的某个本地端口,您可以调用bind(sock, &sa, sa_len)并将sockaddr归零。 只需设置您需要的家庭。 它会将套接字默认绑定到系统选择的某个端口。

sockaddr_in sa = {};
sa.sin_family = AF_INET;
const int res = ::bind(socket_id, (sockaddr*) &sa, sizeof sa);
if (res < 0)
{
    std::cerr << "UDP binding has failed\n";
    return;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM