C++ 我是否必须在客户端程序中绑定 UDP 套接字才能接收数据?(我总是得到 WSAEINVAL)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3057029/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do I have to bind a UDP socket in my client program to receive data? (I always get WSAEINVAL)
提问by Incubbus
I am creating a UDP socket (AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
) via Winsock and trying to recvfrom
on this socket, but it always returns -1 and I get WSAEINVAL (10022). Why?
我正在通过 Winsock创建一个 UDP 套接字 ( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
) 并尝试recvfrom
在这个套接字上创建,但它总是返回 -1 并且我得到 WSAEINVAL (10022)。为什么?
When I bind()
the port, that does not happen, but I have read that it is very lame to bind the client's socket.
当我bind()
使用端口时,这不会发生,但我读到绑定客户端的套接字非常蹩脚。
I am sending data to my server, which answers, or at least, tries to.
我正在将数据发送到我的服务器,它会回答,或者至少会尝试。
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;
}
...
This is a working example I wrote a few moments ago, and it works without the call to bind()
in the client:
这是我刚才写的一个工作示例,它无需调用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] = '##代码##';
std::cout << "Received: " << Buffer << std::endl;
}
return 0;
}
回答by Warren Young
With UDP, you have to bind()
the socket in the client because UDP is connectionless, so there is no other way for the stack to know which program to deliver datagrams to for a particular port.
使用 UDP,您必须bind()
使用客户端中的套接字,因为 UDP 是无连接的,因此堆栈没有其他方法可以知道将数据报传送到特定端口的哪个程序。
If you could recvfrom()
without bind()
, you'd essentially be asking the stack to give your program all UDP datagrams sent to that computer. Since the stack delivers datagrams to only one program, this would break DNS, Windows' Network Neighborhood, network time sync....
如果recvfrom()
没有bind()
,您实际上是在要求堆栈向您的程序提供发送到该计算机的所有 UDP 数据报。由于堆栈只向一个程序传送数据报,这会破坏 DNS、Windows 的网上邻居、网络时间同步......
You may have read somewhere on the net that binding in a client is lame, but that advice only applies to TCP connections.
您可能已经在网络上的某个地方读到过客户端中的绑定是蹩脚的,但该建议仅适用于 TCP 连接。
回答by Mecki
Your other code sample works because you are using sendto
before recvfrom
. If a UDP socket is unbound and either sendto
or connect
are called on it, the system will automatically bind it for you and thus the recvfrom
call later on will succeed. recvfrom
will not bind a socket, as this call expects the socket to have been bound already, or an error will be thrown.
您的其他代码示例有效,因为您sendto
之前使用的是recvfrom
. 如果 UDP 套接字未绑定并且在其上调用sendto
或connect
调用,系统将自动为您绑定它,因此recvfrom
稍后调用将成功。recvfrom
不会绑定套接字,因为此调用期望套接字已被绑定,否则将引发错误。
回答by Stradivari
I had the same problem a few weeks ago, the following remarks helped me to understand whether an explicit bind() call is necessary:
几周前我遇到了同样的问题,以下评论帮助我了解是否需要显式 bind() 调用:
Explicit binding is discouraged for client applications. For client applications using this function, the socket can become bound implicitly to a local address through sendto, WSASendTo, or WSAJoinLeaf.
不鼓励对客户端应用程序进行显式绑定。对于使用此函数的客户端应用程序,套接字可以通过sendto、WSASendTo或WSAJoinLeaf隐式绑定到本地地址。
NoteIf a socket is opened, a setsockopt call is made, and then a sendtocall is made, Windows Sockets performs an implicit bindfunction call. If the socket is unbound, unique values are assigned to the local association by the system, and the socket is then marked as bound.
注意如果打开了一个套接字,则进行了 setsockopt 调用,然后进行了sendto调用,Windows 套接字将执行隐式绑定函数调用。如果套接字未绑定,系统会为本地关联分配唯一值,然后将套接字标记为绑定。
回答by Eugen Constantin Dinca
Hereit says the following:
这里有以下内容:
Parameters
参数
s [in]: A descriptor identifying a bound socket.
s [in]:标识绑定套接字的描述符。
...
...
Return Value
返回值
WSAEINVAL: The socket has not been bound with bind, or an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, or (for byte stream-style sockets only) len was zero or negative.
WSAEINVAL:套接字尚未绑定绑定,或者指定了未知标志,或者为启用 SO_OOBINLINE 的套接字指定了 MSG_OOB,或者(仅对于字节流样式的套接字)len 为零或负数。
As far as I remember bind is not required for a UDP socket because a bind call is made for you by the stack. I guess it's a Windows thing to require a bind on a socket used in a recvfrom call.
据我所知,UDP 套接字不需要绑定,因为堆栈为您进行了绑定调用。我想这是 Windows 的事情,需要在 recvfrom 调用中使用的套接字上进行绑定。