[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 falschViele 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