Je crée un socket UDP (AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
) Via Winsock et j'essaie de recvfrom
sur ce socket, mais il renvoie toujours -1 et Je reçois WSAEINVAL (10022). Pourquoi?
Lorsque je bind()
le port, cela ne se produit pas, mais j'ai lu qu'il est très boiteux de lier le socket du client.
J'envoie des données à mon serveur, qui répond, ou du moins essaie.
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;
}
...
Ceci est un exemple de travail que j'ai écrit il y a quelques instants, et cela fonctionne sans l'appel à bind()
dans le client:
#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;
}
Avec UDP, vous devez bind()
le socket dans le client car UDP est sans connexion, il n'y a donc pas d'autre moyen pour la pile de savoir à quel programme envoyer des datagrammes pour un port particulier.
Si vous pouviez recvfrom()
sans bind()
, vous demanderiez essentiellement à la pile de donner à votre programme tous les datagrammes UDP envoyés à cet ordinateur. Étant donné que la pile fournit des datagrammes à un seul programme, cela romprait le DNS, le voisinage réseau de Windows, la synchronisation de l'heure du réseau ...
Vous avez peut-être lu quelque part sur le net que la liaison dans un client est boiteuse, mais ce conseil ne s'applique qu'aux connexions TCP.
Votre autre exemple de code fonctionne car vous utilisez sendto
avant recvfrom
. Si un socket UDP n'est pas lié et que sendto
ou connect
sont appelés dessus, le système le liera automatiquement pour vous et donc l'appel recvfrom
plus tard réussira. recvfrom
ne liera pas de socket, car cet appel s'attend à ce que le socket soit déjà lié, ou une erreur sera levée.
J'ai eu le même problème il y a quelques semaines, les remarques suivantes m'ont aidé à comprendre si un appel bind () explicite est nécessaire:
La liaison explicite est déconseillée pour les applications clientes. Pour les applications clientes utilisant cette fonction, le socket peut être lié implicitement à une adresse locale via sendto , WSASendTo , ou WSAJoinLeaf .
Remarque Si un socket est ouvert, un appel setsockopt est effectué, puis un sendto l'appel est effectué, Windows Sockets effectue un appel de fonction implicite bind . Si le socket n'est pas lié, des valeurs uniques sont attribuées à l'association locale par le système, puis le socket est marqué comme lié.
Ici il dit ce qui suit:
Paramètres
s [in]: descripteur identifiant une socket liée.
...
Valeur de retour
WSAEINVAL: le socket n'a pas été lié avec bind, ou un indicateur inconnu a été spécifié, ou MSG_OOB a été spécifié pour un socket avec SO_OOBINLINE activé, ou (pour les sockets de style flux d'octets uniquement) len était zéro ou négatif.
Pour autant que je me souvienne, la liaison n'est pas requise pour un socket UDP car un appel de liaison est effectué pour vous par la pile. Je suppose que c'est une chose Windows d'exiger une liaison sur un socket utilisé dans un appel recvfrom.