[Anfänger] Verbindungsprobleme bei Sockets



  • Liebe Forumsgemeinde!

    Ich beschäftige mich nun seit kurzem mit der Socket-Programmierung in C++ und bin gerade dabei, eine kleine simple Übung, ein Chat-Programm mit Server/Client Modell zu schreiben. Dabei habe ich mich an das Tutorial auf http://www.c-worker.ch/tuts/wstut_op.php gehalten.

    Hier sind meine Codebeispiele
    Server:

    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <stdio.h>
    #include <iostream>
    #pragma comment(lib, "Ws2_32.lib")
    using namespace std;
    
    int startWinsock(void)
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2, 0), &wsa);
    }
    
    int main()
    {
    	long rc;
    	SOCKET acceptSocket;
    	SOCKET connectedSocket;
    	SOCKADDR_IN addr; // Winsock starten
    	rc = startWinsock();
    	// Socket erstellen
    	acceptSocket = socket(AF_INET, SOCK_STREAM, 0);
    	memset(&addr, 0, sizeof(SOCKADDR_IN));
    	addr.sin_family = AF_INET;
    	addr.sin_port = htons(12345);
    	addr.sin_addr.s_addr = ADDR_ANY;
    	rc = bind(acceptSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN));
    	rc = listen(acceptSocket, 10);
    	connectedSocket = accept(acceptSocket, NULL, NULL);
    	system("pause");
    }
    

    Und für den Client:

    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <stdio.h>
    #include <iostream>
    #pragma comment(lib, "Ws2_32.lib")
    using namespace std;
    
    int startWinsock(void)
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2, 0), &wsa);
    }
    
    int main()
    {
    	long rc;
    	rc = startWinsock();
    	SOCKET s;
    	SOCKADDR_IN addr;
    	s = socket(AF_INET, SOCK_STREAM, 0);
    	memset(&addr, 0, sizeof(SOCKADDR_IN)); // zuerst alles auf 0 setzten 
    	addr.sin_family = AF_INET;
    	addr.sin_port = htons(12345); // wir verwenden mal port 12345
    	addr.sin_addr.s_addr = inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr)); // zielrechner ist unser eigener 
    // Da VS 2013 inet_addr nicht mag, durch inet_pton ersetzbar??
    	rc = connect(s, (SOCKADDR*)&addr, sizeof(SOCKADDR));
    	if (rc == SOCKET_ERROR)
    	{
    		cout << "Fehler: Verbindung gescheitert, Fehlercode: " << WSAGetLastError() << endl;
    	}
    	else
    	{
    		cout << "Verbunden mit 127.0.0.1.." << endl;
    	}
    	system("pause");
    
    }
    

    Wenn ich jetzt den Server starte und dann den Client anwerfe, bringt der Client immer eine Fehlermeldung, nämlich Error 10060.
    "Connection timed out.
    Fehler beim Herstellen der Verbindung, da die Gegenstelle nach einer bestimmten Zeitspanne nicht ordnungsgemäß reagiert hat, oder die hergestellte Verbindung konnte nicht aufrecht erhalten werden, da der verbundene Host nicht reagiert hat." (Windows Socket Fehlercodes)
    Woran kann das denn liegen? 😕
    Die beiden Programme liegen jetzt zum Testen am selben Rechner, im selben Ordner; der PC befindet sich in meinem Wlan und greift mittels Router auf das Internet zu. Der einzige Teil des Codes, den ich selbst noch stärker editieren musste, ist im Client auf Zeile 24, hier ist im original vom Tutorium die Funktion inet_addr benutzt, welche mir allerdings Visual Studio als Fehler ausgibt und empfiehlt, ich solle sie durch inet_pton ersetzen. Ist inet_pton in diesem Fall gleichwertig?
    Wenn ich meinen PC aus dem WLan ausklinke, gibt mir der Client den Fehlercode 10065, das sollte aber so sein, weil er ja nicht mehr der Adresse zum Router und wieder zum PC folgen kann. Oder habe ich da etwas falsch verstanden?
    Fällt euch bei meinem Code irgendwas auf? Oder liegt es an meinem System?

    Ich habe schon einige Foren angesehen und herumgegoogled, eine richtige Lösung habe ich allerdings nicht finden können.
    Ich hoffe, der Beitrag ist hier nicht falsch 🙂

    Viele Grüße und danke
    Eteokles



  • Eteokles schrieb:

    Ich hoffe, der Beitrag ist hier nicht falsch 🙂

    Du hast keine Frage zu C++, sondern zum Windows Socket C API => er ist hier falsch.

    Wenn du dich nicht auf Windows beschränken willst, schau dir boost asio an.



  • Hi,

    ich rufe in meiner Implementierung zwischen listen und accept noch select auf. Bin mir aber nicht sicher, ob das wirklich notwendig ist, denke aber schon. Darüber hinaus prüfe ich auch Rückgabewerte auf Fehler.

    So sieht's bei mir aus:

    void SocketServerBase::runListenerThread( unsigned short port )
    {
    	doEnterFunction("SocketServerBase::runListenerThread");
    
    	timeval			timeout = { 10, 0 };
    	fd_set			sockets;
    	SOCKET 			clientSocket;
    	SOCKADDR_IN		serverAddress, clientAddress;
    	socklen_t		clientSize;
    
    	this->port = port;
    
    	/*
    		create a socket
    	*/
    	if( !socketError )
    	{
    		theSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    		if( theSocket == INVALID_SOCKET )
    			socketError = -1;
    	}
    
    	if( !socketError )
    	{
    		memset( &serverAddress, 0, sizeof(serverAddress) );
    		serverAddress.sin_addr.s_addr = INADDR_ANY;
    		serverAddress.sin_family = AF_INET;
    		serverAddress.sin_port = htons( port );
    
    		socketError = bind( theSocket, (SOCKADDR*)&serverAddress, sizeof(serverAddress) );
    	}
    
    	if( !socketError )
    	{
    		socketError = listen( theSocket, SOMAXCONN );
    	}
    
    	if( !socketError )
    	{
    		connected = true;
    		terminated = false;
    		while( !terminated )
    		{
    #ifdef __GNUC__
    			memset( &sockets, 0, sizeof( sockets ) );
    #else
    			sockets.fd_count = 0;
    #endif
    			FD_SET( theSocket, &sockets );
    			select( theSocket+1, &sockets, NULL, NULL, &timeout );
    			if( FD_ISSET( theSocket, &sockets ) )
    			{
    				clientSize = sizeof( clientAddress );
    				clientSocket = accept(
    					theSocket, (SOCKADDR*)&clientAddress, &clientSize
    				);
    				if( clientSocket != INVALID_SOCKET )
    				{
    					doLogPosition();
    					new SERVER_THREAD(
    						createNewServer( this, clientSocket, clientAddress )
    					);
    					THREAD::CheckThreadCount();
    					doLogPosition();
    				}
    			}
    		}
    		if( !killed )
    		{
    			// wait until my server threads are shut down
    			while( THREAD::CheckThreadCount( true ) )
    			{
    				doLogPosition();
    				Sleep( 1000 );
    			}
    		}
    	}
    
    	if( socketError )
    	{
    		socketError = WSAGetLastError();
    		theErrorText = "Listen failed";
    	}
    
    	disconnect();
    
    	doShowLog();
    }
    

    Die Debugfunktionen (die mit do anfangen) kanns't Du löschen. Die Threadimpklementierung mußt Du natürlich anpassen.

    mfg Martin



  • ...



  • inet_pton nutzt den Rückgabewert um Erfolg oder Fehler anzuzeigen.
    Die Adresse wird nur in den übergebenen Buffer geschrieben.

    ( select ist normal nicht nötig. Es dient nur dazu, um festzustellen ob ein Socket lesbar ist ( accept würde sonst blockieren bis einer Verbindung eingeht). Weiterer Vorteil: Es können mehrere Sockets überprüft werden um danach nur die zu bearbeiten, die nicht blockieren würden.



  • So, jetzt funktioniert es wirklich, vielen Dank dafür!! 👍 😃
    Es war einfach der falsche Einsatz der Funktion inet_pton, von der ich den Rückgabewert verwendet hab und nicht den Wert aus dem Puffer.

    Viele Grüße
    Eteokles


Anmelden zum Antworten