Я создаю сокет 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;
}
С UDP вы должны bind ()
сокет в клиенте, потому что UDP не поддерживает соединение, поэтому у стека нет другого способа узнать, какой программа для доставки дейтаграмм для определенного порта.
Если бы вы могли recvfrom ()
без bind ()
, вы, по сути, просили бы стек передать вашей программе все дейтаграммы UDP, отправляемые на этот компьютер. Поскольку стек доставляет дейтаграммы только одной программе, это нарушит DNS, сетевое окружение Windows, синхронизацию сетевого времени ....
Возможно, вы читали где-то в сети, что привязка в клиенте неуместна, но это только совет. применяется к TCP-соединениям.
Здесь говорится следующее:
Параметры
s [in]: дескриптор, идентифицирующий связанный сокет.
...
Возвращаемое значение
WSAEINVAL: сокет не был связан с привязкой, или был указан неизвестный флаг, или MSG_OOB был указан для сокета с включенным SO_OOBINLINE, или (для стиля байтового потока только сокеты) len было нулевым или отрицательным.
Насколько я помню, привязка не требуется для сокета UDP, потому что вызов привязки выполняется за вас стеком. Я предполагаю, что это вещь Windows, требующая привязки к сокету, используемому в вызове recvfrom.