我正在通过Winsock创建UDP套接字(AF_INET
,SOCK_DGRAM
,IPPROTO_UDP
)并在此套接字上尝试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;
}
答案 0 :(得分:38)
使用UDP,您必须bind()
客户端中的套接字,因为UDP是无连接的,因此堆栈无法知道将数据报传递给特定端口的程序。
如果你recvfrom()
没有bind()
,你基本上要求堆栈给你的程序发送到该计算机的所有UDP数据报。由于堆栈只将数据报传递给一个程序,这会破坏DNS,Windows的网络邻居,网络时间同步....
您可能已经在网上某处读到客户端绑定很蹩脚,但该建议仅适用于TCP连接。
答案 1 :(得分:28)
您的其他代码示例有效,因为您在sendto
之前使用recvfrom
。如果UDP套接字未绑定且在其上调用sendto
或connect
,则系统将自动为您绑定它,因此稍后recvfrom
调用将成功。 recvfrom
不会绑定套接字,因为此调用期望套接字已被绑定,否则将引发错误。
答案 2 :(得分:3)
几周前我遇到了同样的问题,以下评论帮助我理解是否需要显式的bind()调用:
不鼓励客户端应用程序使用显式绑定。对于使用此功能的客户端应用程序,套接字可以通过 sendto , WSASendTo 或 WSAJoinLeaf 隐式绑定到本地地址。
注意如果打开套接字,则进行setsockopt调用,然后进行 sendto 调用,Windows套接字执行隐式绑定函数调用。 如果套接字未绑定,则系统会将唯一值分配给本地关联,然后将套接字标记为绑定。
答案 3 :(得分:1)
Here它说了以下内容:
参数
s [in]:标识绑定套接字的描述符。
...
返回值
WSAEINVAL:套接字尚未绑定绑定,或者指定了未知标志,或者为启用了SO_OOBINLINE的套接字指定了MSG_OOB,或者(仅适用于字节流样式套接字)len为零或负数。
据我所知,UDP套接字不需要绑定,因为堆栈会为您调用绑定。我想在recvfrom调用中使用的套接字上需要绑定是一件Windows事情。