C++: Udp mit select() & Timeout



  • Hallo zusammen,

    ich möchte auf meinem PC 2 Programme laufen lassen, die sich gegenseitig Nachrichten schicken. (Zu Lernzwecken ;))
    Untenstehendes Programm soll eine Nachricht senden (sendto()) und dann per select() auf eine Antwort warten und diese ggf. empfangen.
    Der Timeout von select() schlägt das 1. mal korrekt zu, danach bekomme ich immer eine "1" zurück und empfange somit über den zugehörigen Socket Müll.
    Das passiert jedoch nur, wenn ich als Ip meine "127.0.0.1" nehmen, gebe ich etwas anderes ein, klappt das mit dem Timeout super. Ich hoffe mir kann jemand helfen:

    //--gekürtzter Code
    WSADATA wsa;
    SOCKADDR_IN RemoteAddr;
    SOCKADDR_IN OwnAddr;
    SOCKET UDP_Socket1;
    fd_set m_Fds;
    struct timeval m_Timeout;
    int iRemoteAddrLenght = sizeof(SOCKADDR_IN);
    int i = 0;
    
    //--Init
    WSAStartup (MAKEWORD (2,2), &wsa);
    UDP_Socket1 = socket(AF_INET, SOCK_DGRAM, 0);
    m_Timeout.tv_sec = 2;
    m_Timeout.tv_usec = 0;
    
    RemoteAddr.sin_family = AF_INET;
    RemoteAddr.sin_port = htons (2002);
    RemoteAddr.sin_addr.s_addr = inet_addr("127.0.0.1");	
    
    OwnAddr.sin_family = AF_INET;
    OwnAddr.sin_port = htons (2003);
    OwnAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    bind(UDP_Socket1, (SOCKADDR*) &OwnAddr, sizeof(OwnAddr));
    
    for(;;)
    {
        //..//
        //--senden
        sendto(UDP_Socket1, sSend.c_str(), strlen(sSend.c_str()), 0 , (SOCKADDR*)&RemoteAddr, sizeof(RemoteAddr));
    
        //--select & empfangen
        FD_ZERO(&m_Fds);
        FD_SET(UDP_Socket1,&m_Fds);
        i = select(sizeof(m_Fds)*8, &m_Fds, NULL, NULL, &m_Timeout);
        if(i > 0)
        {
            recvfrom(UDP_Socket1, m_szBuff, 256, 0, (SOCKADDR*) &RemoteAddr, &m_iRemoteAddrLenght); 
        } //if
        else if(i < 1)        // klappt nur beim 1. Schleifendurchlauf, danach ist i immer = 1
        {
            cout << "Udp Timeout" << endl;    
        } //else if
    } //for
    


  • Bist Du sicher, dass das richtig ist:

    select(sizeof(m_Fds)*8, &m_Fds, NULL, NULL, &m_Timeout);
    

    Sollte da nicht eine schnöde 1 stehen als erster Parameter?

    Ausserdem: warum prüfst Du i nur auf <1 und >0 und nicht explizit auf >0(data), 0(timeout), -1(error)?



  • Was soll das im Linux/Unix-Forum? WSAStartup? Bitte weg hier! Das ist des Teufels.



  • auch mit ner 1 am Anfang änder sich da leider nix, außerdem möchte ich später ja auch mehrere sockets mit select() bedienen.
    (die Prüfung hab ich in dem codeschnipsel rausgenommen, im richtigen Programm ist nat. alles abgesichert)

    ok, sorry, aber wohin denn dann?
    er hier hat einen aufn deckel bekommen, weil er nich unter linux war...
    http://www.c-plusplus.net/forum/18167-full



  • moon12 schrieb:

    ok, sorry, aber wohin denn dann?
    er hier hat einen aufn deckel bekommen, weil er nich unter linux war...
    http://www.c-plusplus.net/forum/18167-full

    Nein, es war einfach kein Standard-C++ und die Leute im Forum dort dachten, er sei Unixer. War aber wohl unter Windows, ergo WinAPI oä.



  • Dieser Thread wurde von Moderator/in nman aus dem Forum Linux/Unix in das Forum WinAPI verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • keiner der mir helfen kann?
    oder ist meine frage unklar?



  • Was gibt recvfrom denn zurück?



  • mit WSAStartup (MAKEWORD (2,2), &wsa):
    in der allerersten Schleife meldet select()korrekt einen Timeout, danach nicht mehr und recvfrom() liefert 4lachende Smileys gefolgt von "WinSock 2.0" (das mit den Smileys ist kein Witz ! 🙂

    mit WSAStartup (MAKEWORD (2,0), &wsa):
    Das gleiche Verhalten, allerdings nur ein einzelnes Smiley, sonst nix



  • Was gibt recvfrom denn zurück?



  • sry

    recvfrom: -1
    WSAGetLastError: 10054



  • moon12 schrieb:

    recvfrom: -1

    Also ein Fehler. Dann solltest du nichts ausgeben, da kann ja nur Quark kommen.

    moon12 schrieb:

    WSAGetLastError: 10054

    Doku sagt:

    WSAECONNRESET: On a UDP-datagram socket this error indicates a previous send operation resulted in an ICMP Port Unreachable message.



  • das da mist rauskommt ist ja klar, mein programm hat ja auch nichts empfangen sondern nur selbst gesendet.
    Meine Frage war ja auch, warum select() zuvor keinen Timeout meldet, sondern eben eine 0, was überhaupt erst dazu führt, dass recvfrom() aufgerufen wird.

    und was das WSAECONNRESET betrifft:
    Was genau bedeutet das? nach meinem bisherigen verständnis ist doch udp verbindungslos, also wird eine nachricht einfach an einen Port gefeuert und danach sich nicht mehr drum gekümmert oder nicht? wie sollte das also mein select() beeinflussen?



  • Wsa gibt denn sendto zurück?



  • 56
    also die Anzahl an gesendeten Bytes
    WSAGETLASTERROR bei sendto: 0



  • Ok, ich kann es reproduzieren, aber erklären kann ich's nicht. 😕



  • hm immerhin ^^
    aber ist mein ansatz sonst (mit ausnahme, dass ich an localhost sende) in ordnung?
    oder würde man senden & empfangen in dem verbund generell anders realisieren?



  • Nutz beim Initialisieren mal Winsock 2.0
    Mach den ersten Parameter bei select zu 0 (der wird unter Windows nicht benötigt)

    Ist dein Puffer groß genug?

    Und dann mach mal ein kompilierbares Beispiel, ansonsten hab ich keine Lust mir das genauer anzuschauen...



  • frisch aufgesetzt:

    // Udp: senden->empfangen per select()
    
    # pragma comment(lib, "Ws2_32.lib")
    # include <string>
    # include <iostream>																			
    # include <WinSock2.h>																		
    # include <Windows.h>																		
    using namespace std;
    
    int main()
    {
    	//--Variablenliste
    	int iRemoteAddrLenght = sizeof(SOCKADDR_IN);
    	long rc;
    	SOCKET UdpSocket;	
    	char szBuffer[256] = {0};
    	SOCKADDR_IN RemoteAddr;	
    	SOCKADDR_IN OwnAddr;
    	fd_set Fds;
    	struct timeval Timeout;
    	Timeout.tv_sec = 2;
    	Timeout.tv_usec = 0;
    	string sSend = "Teststring";
    	string sRecv;
    
    	//--Winsock
    	WSADATA wsa;																	
    	rc =  WSAStartup (MAKEWORD (2,0), &wsa);
    	if (rc != 0)
    	{
    		cout << "Fehler: startWinsock, Fehlercode: " << rc << endl;
    	} //if
    	else
    	{
    		cout << "Winsock gestartet!" << endl;
    	} //else
    
    	//--Socket
    	UdpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    	if (UdpSocket == INVALID_SOCKET)
    	{
    		cout << "Fehler: Socket konnte nicht erstellt werden, Fehlercode: " << WSAGetLastError() << endl;
    	} //if
    	else
    	{
    		cout << "Socket erstellt!" << endl;
    	} //else
    
    	//--SOCKADDR_IN
    	RemoteAddr.sin_family = AF_INET;																
    	RemoteAddr.sin_port = htons (2002);			
    	RemoteAddr.sin_addr.s_addr = inet_addr("127.0.0.1");	
    
    	OwnAddr.sin_family = AF_INET;
    	OwnAddr.sin_port = htons (2003);
    	OwnAddr.sin_addr.s_addr = ADDR_ANY;
    
    	//--bind: OwnAddr
    	rc = bind(UdpSocket, (SOCKADDR*) &OwnAddr, sizeof(OwnAddr));
    	if (rc == SOCKET_ERROR)
    	{
    		cout << "Fehler: bind, Fehlercode: " << WSAGetLastError() << endl;
    	} //if
    	else
    	{
    		cout << "Socket an Port " << htons(OwnAddr.sin_port) << " gebunden!" << endl;
    	} //else
    
    	//--Endlosschleife mit senden + empfangen
    	for(;;)
    	{
    		//--sendto
    		rc = sendto(UdpSocket, sSend.c_str(), strlen(sSend.c_str()), 0 , (SOCKADDR*) &RemoteAddr, sizeof(RemoteAddr));
    		cout << "return sendto: " << rc << endl;
    		if (rc == SOCKET_ERROR)
    		{
    			cout << "Fehler: sendto, Fehlercode: " << WSAGetLastError() << endl;
    		} //if
    		else
    		{
    			cout << "sendto, gesendet: " << sSend << endl;
    			cout << rc << " Bytes gesendet!" << endl;
    		} //else
    
    		//--select
    		FD_ZERO(&Fds);
    		FD_SET(UdpSocket,&Fds);
    		rc = select(0, &Fds, NULL,NULL, &Timeout);
    		cout << "return select: " << rc << endl;
    		if(rc == -1)
    		{
    			cout << "Fehler: select, Fehlercode: " << WSAGetLastError() << endl;
    		} //if
    		else if(rc == 0)
    		{
    			cout << "Fehler: select timeout" << endl;
    			cout << "select timeout Fehlercode: " << WSAGetLastError() << endl;
    		} //else if
    		else if(rc > 0)
    		{
    			if(FD_ISSET(UdpSocket, &Fds))
    			{
    				rc = recvfrom(UdpSocket, szBuffer, 256, 0, (SOCKADDR*) &RemoteAddr, &iRemoteAddrLenght); 
    				cout << "return recvfrom: " << rc << endl;
    				if (rc == SOCKET_ERROR)
    				{
    					cout << "Fehler: recvfrom, Fehlercode: " << WSAGetLastError() << endl;
    				} //if
    				else
    				{
    					sRecv = szBuffer;
    					cout << " Empfangen: " << sRecv << endl;
    					cout << rc << " Bytes empfangen!" << endl;
    				} //else				
    			} //if
    		} //else if
    
    		cout << endl << "----------------------------------------" << endl;
    
    	} //for
    
    	closesocket(UdpSocket);
    	WSACleanup();
    	return 0;
    
    } //main()
    

    Wobei ich rausgefunden habe, dass das Problem immer auftritt, wenn ich den Socket auf der Gegenseite nicht eingerichtet habe, egal bei welcher Ip im Netzwerk.



  • Lang ists her...

    Die Lösung ist:

    select() verändert die Einstellungen bei jedem Durchlauf.

    Man muss das
    Timeout.tv_sec = 2;
    Timeout.tv_usec = 0;

    innerhalb der Endlosschleife neu setzen.

    Dank, natürlich, an Stackoverflow.