[Gelöst] ICMP Port Unreachable nach sendto()



  • Hallo Leute!

    Es geht um folgenden Quellcode:

    #include <WinSock2.h>
    #include <WS2tcpip.h>
    #include <iostream>
    
    int main(){
    
    	char* statusMessage = "\xFF\xFF\xFF\xFFgetstatus\x0A";
    
    	WSADATA wsaData;
    	WSAStartup(MAKEWORD(2,2), &wsaData);
    
    	SOCKET networkSocket;
    	char buffer[2048];
    
    	struct addrinfo* serviceResults;
    	struct addrinfo serviceInfo;
    
    	ZeroMemory(&serviceInfo,sizeof(serviceInfo));
    
    	serviceInfo.ai_family = AF_INET;
    	serviceInfo.ai_socktype = SOCK_DGRAM;
    	serviceInfo.ai_protocol = IPPROTO_UDP;
    
    	int result;
    	if((result = getaddrinfo("178.63.72.120","27960",&serviceInfo,&serviceResults)) !=0)
    		std::cout << "getaddrinfo() - Error: " << result << std::endl;
    
    	if((networkSocket = socket(serviceResults->ai_family,serviceResults->ai_socktype,serviceResults->ai_protocol)) == INVALID_SOCKET)
    		std::cout << "socket() - Error: " << WSAGetLastError() << std::endl;
    
    	if((result = sendto(networkSocket,statusMessage,strlen(statusMessage),0,serviceResults->ai_addr,serviceResults->ai_addrlen)) == SOCKET_ERROR)
    		std::cout << "sendto() - Error: " << WSAGetLastError() << std::endl;
    	else
    		std::cout << "sendto() - Bytes: " << result << std::endl;
    
    	if((result = recvfrom(networkSocket,buffer,2048-1,0,NULL,NULL)) > 0){
    		buffer[result]='\0';
    		std::cout << "recvfrom() - Bytes: " << result << std::endl;
    		std::cout << "Buffer: " << std::endl;
    		std::cout << buffer << std::endl;
    	}
    	else if(result==-1)
    		std::cout << "recvfrom() - Error: " << WSAGetLastError() << std::endl;
    
    	system("PAUSE");
    
    	freeaddrinfo(serviceResults);
    	closesocket(networkSocket);
    	WSACleanup();
    
    	return 0;
    }
    

    Ich wollte in C++ einen Server welches das Quake3 Protokoll verwendet nach Status Informationen abfragen. Allerdings erhalte ich von recvfrom() -1 zurück und laut WSAGetLastError() handelt es sich um den Fehler 10054 (WSAECONNRESET).
    Ich habe erst vermutet das die UDP Antwort Packete nicht mehr zurück kommen wegen der Firewall. Allerdings habe ich das selbe in Python implementiert und da funktioniert es einwandfrei (ohne an der Firewall rumzuspielen). Sollte doch eigentlich so passen? Oder muss ich doch ein bind() aufrufen? Das habe ich auch schon probiert aber ich kriege eine andere Fehlermeldung (10049 - WSAEADDRNOTAVAIL, was vielleicht daran liegen könnte das in getaddrinfo() die IP des Spielservers drinsteht und nicht die des lokalen Computers?)

    Ich hatte schon einmal etwas ähnliches geschrieben und ich meine es lief ohne bind(), allerdings habe ich die Quellcodes nicht mehr.

    Habe das auch mit Wireshark nachgeprüft, wenn ich den quake3 Filter drinhabe sehe ich das er die statusabfrage erfolgreich sendet aber anschließend ein destination unreachable (port unreachable) empfängt.

    edit: Hab nun versucht über connect() eine verbindung herzustellen so das ich send/recv(statt sendto/recvfrom) verwenden kann, allerdings wieder der selbe fehler.



  • Pruefe bitte die Rueckgabewerte der anderen Funktionen. Normalerweise kannst du keinen Socket erstellen, der nicht lokal ist.



  • Habe nun die Rückgabewerte der anderen Funktionen ebenfalls überprüft, dort passt alles soweit, 14 Bytes werden gesendet aber weiterhin bei recvfrom mit -1 als Rückgabewert.

    edit:
    Habe den Quellcode nochmal aktualisiert, mit der Überprüfung der Rückgabewerten



  • Vergleich mal mit Wireshark die C und die Python Variante.
    Da muss in dem Paket das rausgeht irgendwas anders sein.
    Und guck mit dem Debugger nach was du in serviceResults zurückbekommst - ob das auch das ist was du erwartest.



  • RTFM:

    int recvfrom(
    __in SOCKET s,
    __out char *buf,
    __in int len,
    __in int flags,
    __out struct sockaddr *from,
    __inout_opt int *fromlen
    );

    Parameter
    s [in]
    A descriptor identifying a bound socket.
    ...

    Explizit: A descriptor identifying a bound socket. Und wie bindet man einen Socket? .... bind.

    könnte das in getaddrinfo() die IP des Spielservers drinsteht und nicht die des lokalen Computers?

    Dann must du wohl zwei Adressen verwenden, eine lokale und eine fur dein Ziel.

    PS: Wild rumprobieren hilft nicht. http://beej.us/guide/bgnet/



  • Ob dein Code stimmt oder nicht, weiss ich nicht. Fakt ist: der Server antwortet nicht. Hab das mit meinem Q3 Querytool ausprobiert.

    Hier haste mal ne Liste von Q3 Server die auf jeden Fall antworten sollten:

    http://www.gametracker.com/search/q3/?searchipp=50#search

    Du brauchst weder bind noch connect um die Abfrage durchzuführen.



  • Du brauchst weder bind noch connect um die Abfrage durchzuführen.

    Aehm ... was wird wohl das Query-Tool machen?



  • Danke für den Hinweis, habe das nun an anderen Servern (Quake 3 und Wolfenstein ET) ausprobiert und da erhalte ich die gewünschte Antwort vom Server (ohne bind() und connect()). Der Server ist wohl nicht mehr verfügbar, hatte den auch auf gametracker.com entdeckt aber nun ist es nicht mehr in der Server-Liste zu finden.
    Vielen Dank aber für eure Antworten und Tipps!



  • ohne bind() und connect

    Ich habe nicht connect geredet. Ich verstehe aber trotzdem nicht, warum es funktionieren sollte. Erklaere doch mal bitte.

    The socket function causes a socket descriptor and any related resources to be allocated and bound to a specific transport-service provider.

    Ist das spezifisch fuer Windows oder portabel?



  • knivil schrieb:

    ohne bind() und connect

    Ich habe nicht connect geredet. Ich verstehe aber trotzdem nicht, warum es funktionieren sollte. Erklaere doch mal bitte.

    Das mit connect() bezog sich auf den Beitrag von "................." weil er erwähnt hat das ich das nicht benötige.

    Ich hatte bevor ich mein Code hier geposted habe schon in Beej's Network Programming Guide an der folgenden Stelle bereits gedacht das bind() in meinem Fall eigentlich nicht notwendig sein sollte:

    If you haven't yet called bind() on the socket descriptor, it is automatically bound to your IP
    address and a random local port. This is usually just fine with you if you're not a server, since you
    really don't care what your local port is; you only care what the remote port is so you can put it in the
    serv_addr parameter. You can call bind() if you really want your client socket to be on a specific IP
    address and port, but this is pretty rare.

    Da dachte ich erst das sich das vielleicht nur auf Socket-Programmierung unter Linux bezieht. Aber auf der MSDN Seite steht bei der Beschreibung zur recvfrom() Funktion weiterhin:

    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.

    Ich interpretiere das nun so das ein erfolgreicher Aufruf von sendto() einen Client Socket automatisch an meine lokale Adresse bindet und es somit möglich macht via recvfrom() etwas zu empfangen (ohne bind() extra aufgerufen zu haben).

    Edit:

    Ich denke das sollte es bestätigen (MSDN - sendto()):

    Note If a socket is opened, a setsockopt call is made, and then a sendto call is made, Windows Sockets performs an implicit bind function 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.



  • ... wieder was gelernt. In den man-Pages findet sich nichts konkretes dazu. Wobei

    socket() creates an endpoint for communication ...

    vielleicht dahingehend zu interpretieren ist.


Log in to reply