Sockets, Freigeben funktioniert nicht



  • Hallo,

    habe ein Problem mit dem Freigaben von Sockets. Um das zu demonstrieren hab ich dieses kleine DemoProgramm geschrieben, das das Problem ebenfalls zeigt:

    Unter Windows funktioniert es tendenziell eher, unter Linux ist das Verhalten wie hier beschrieben 100% reproduzierbar.

    Sollte nicht close() den Port wieder freigeben, indem es den Socket davon entbindet?

    void NetWork::socktest()
    {
        	cout<<"Start Sockettest"<<endl;
        	boost::thread serverThread(boost::bind(&NetWork::testServer,this));
    	cout<<"Server gestartet"<<endl;
    	testClient();
    	testClient();
    	testClient();
    	testClient();
    	testClient();
    	serverThread.join();
    }
    
    void NetWork::testClient()
    {
    	int err;
    	int clie_sock_fd;		//Socket
    	struct hostent *server;
    	struct in_addr *server_ip;
    	struct sockaddr_in server_addr;
    	#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    		{boost::mutex::scoped_lock lock(WSA_Sock_Mutex);
    			WSADATA wsa;
    			if (WSAStartup(MAKEWORD(1, 1), &wsa)){
    				out<<"WSAStartup() failed: "<<(unsigned long)GetLastError()<<endl;
    				return;
    			}
    		}
    	#endif
    	{boost::mutex::scoped_lock lock(WSA_Sock_Mutex);
    		clie_sock_fd = socket(PF_INET, SOCK_STREAM, 0);
    		if (clie_sock_fd == -1){
    			cout<<"Can't create new socket"<<endl;
    			return;
    		}
    	}
    	{boost::mutex::scoped_lock lock(WSA_Sock_Mutex);
    	server = gethostbyname("localhost");
    		if (server == NULL){
    				int err = errno;
    			#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    				err = WSAGetLastError();
    			#endif
    			cout<<"Hostname failed. Error:"<<err<<endl;
    			return;
    			}
    	}
    	server_ip = (in_addr *) server->h_addr;
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port = htons(20000);
    	server_addr.sin_addr.s_addr = *((unsigned long *) server_ip);
    	err = connect(clie_sock_fd, (sockaddr*) &server_addr, sizeof(sockaddr_in));
    	if (err == -1){
    		int err = errno;
    		#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    			err = WSAGetLastError();
    		#endif
    		cout<<"connect2: connect() failed. Error:"<<err<<endl;
    		return;
    	}
    
    	char *cptr="test";
    	int errCode = -1;
    	errCode = send(clie_sock_fd, cptr,4,0);
    	if(errCode == -1){
    		#if defined(WIN32) || defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    			cout<<"send_data_raw WSAGetLastError(): "<<WSAGetLastError()<<endl;
    		#else
    			errCode = errno;
    			cout<<"send_data_raw errno: "<<itostr(errCode)<<endl;
    		#endif
    		return;
    	}
    	shutdown(clie_sock_fd,2);
    	close(clie_sock_fd);
    	return;
    }
    
    void NetWork::testServer()
    {
        	for (int n=0;n<5;n++){
    	    	cout<<n<<endl;
    		int err, addr_size;
    		struct sockaddr_in my_addr, client_addr;
    		#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    			{boost::mutex::scoped_lock lock(WSA_Sock_Mutex);
    				WSADATA wsa;
    				if (WSAStartup(MAKEWORD(1, 1), &wsa)){
    					cerr<<"WSAStartup() failed"<<(unsigned long)GetLastError()<<endl;
    					return;
    				}
    			}
    		#endif
    		{boost::mutex::scoped_lock lock(WSA_Sock_Mutex);
    			srv_RX_sock_fd = socket(PF_INET, SOCK_STREAM, 0);
    			if (srv_RX_sock_fd == -1){
    				int err = errno;
    				#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    					err = WSAGetLastError();
    				#endif
    				cout<<"create Socket failed: "<<err<<endl;
    				return;
    			}
    		}
    		my_addr.sin_family = AF_INET;
    		my_addr.sin_port = htons(20000);
    		my_addr.sin_addr.s_addr = INADDR_ANY;
    
    		err = bind(srv_RX_sock_fd, (sockaddr*) &my_addr, sizeof(struct sockaddr_in));
    		if (err == -1){
    			int err = errno;
    			#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    				err = WSAGetLastError();
    			#endif
    			cout<<"bind failed: "<<err<<endl;
    			return;
    		}
    
    		err = listen(srv_RX_sock_fd, NW_N_CONNECTIONS);
    		if (err == -1){
    			int err = errno;
    			#if defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    				err = WSAGetLastError();
    			#endif
    			cout<<"listen failed: "<<err<<endl;
    			return;
    		}
    
    		#if defined(WIN32) || defined(_WIN) || defined(_WIN32) || defined (_WIN64)
    			unsigned long argp = 1;
    			ioctlsocket(srv_RX_sock_fd,FIONBIO,(unsigned long *)&argp);
    		#else
    			fcntl(srv_RX_sock_fd,O_NONBLOCK);
    		#endif
    
    		int acc_sock_fd = accept(srv_RX_sock_fd, (sockaddr*) &client_addr, (socklen_t*) &addr_size);
    		shutdown(acc_sock_fd,2);
    		close(acc_sock_fd);
    
    		shutdown(srv_RX_sock_fd,2);
    		close(srv_RX_sock_fd);
    		cout<<"success"<<endl;
    	}
    	return;
    }
    

    In Ausführung:

    Start Sockettest
    0
    Server gestartet
    success
    1
    bind failed: 98
    connect2: connect() failed. Error:111
    connect2: connect() failed. Error:111
    connect2: connect() failed. Error:111
    connect2: connect() failed. Error:111
    

    Also das 1. mal funktionierts, beim 2 Lauf ist der Port blockiert, obwohl ich den Socket wieder freigebe!?

    Vielen Dank für Hinweise, Grüße
    Mattes



  • Dem ist nicht so. Je nachdem wann von wem die Verbindung geschlossen wird, kann es passieren, dass die Verbindung noch zur Sicherheit "blockiert" bleibt.
    (Schau mal mit netstat nach.)
    Unter Linux kann man das mit SO_LINGER umgehen was aber nicht empfohlen ist.

    Zudem wird normalerweise für einen Server-Sockel mehr als einmal accept aufgerufen, so dass dein "Problem" garnicht auftritt.
    Wieso willst du das denn überhaupt machen?!



  • Mal eine andere Frage:
    Wenn Du eh boost benutzt, wieso machst Du Deine Netzwerkgeschichten nicht gleich mit boost.asio? Das spart die ganzen Defines dann auch ein.



  • Hallo,

    danke für die Antworten!

    lagalopex schrieb:

    Zudem wird normalerweise für einen Server-Sockel mehr als einmal accept aufgerufen, so dass dein "Problem" garnicht auftritt.
    Wieso willst du das denn überhaupt machen?!

    Das Miniprogramm oben diente nur zur Verdeutlichung meines Problemes, weil sich anhand konkreten Codes besser diskutieren lässt.
    In meinem Programm ist accept() natürlich auch in einer Schleife, die solange Verbindungen entgegennimmt, solange Sie läuft.
    Sobald das Programm beendet werden soll, wird eine Variable gesetzt und die Schleife beendet sich. (Der Socket ist auf nicht blockierend geschaltet).
    Wenn ich danach das Programm gleich wieder starte habe ich eben das Problem, dass bind() fehlschlägt (Innerhalb einer Minute etwa).

    Tachyon schrieb:

    Mal eine andere Frage:
    Wenn Du eh boost benutzt, wieso machst Du Deine Netzwerkgeschichten nicht gleich mit boost.asio? Das spart die ganzen Defines dann auch ein.

    Das werde ich mir mal ansehen. Aber ich würde ungern alles umabuen nur um dieses Problem zu lösen...

    Irgendwas muss ich doch falsch machen, bei anderen Programmen funktioniert das ja auch. Hab aber bislang noch nciht rausgefunden, wo mein Fehler ist.

    Grüße
    Mattes



  • hallo,

    hab mir gerade das ganze noch mal per wireshark angeschaut, alle Daten wurden übertragen und die TCP-Verbindung ist ordnungsgemäß geschlossen worden.
    Ob der Socket blockierend arebitet oder nicht, scheint auch keine Auswirkung zu haben.



  • Die Verbindung ist ja auch beendet. Aber das Betriebssystem hält den Port erstmal etwas (2 Minuten?) frei, da es sonst sein kann, dass man in dem neuen Programm noch Daten die für das alte Programm bestimmt waren empfängt.
    Je nach dem, was das Programm macht, ist das egal oder eben weniger egal.
    Mit SO_REUSEADDR kann man das Verhalten abstellen, was aber dann das TCP/IP Protokoll verletzt.

    Wo liegt denn da nun dein Problem? Es ist nunmal so festgelegt worden.
    Und für gewöhlich braucht man ein Programm (mit bind&listen) auch nicht beenden und gleich neustarten. (Außer vielleicht in der Entwicklung.)



  • Hi,

    SO_REUSEADDR funktioniert, Danke.

    lagalopex schrieb:

    Es ist nunmal so festgelegt worden.

    Wenn ich auf meinem Rechner (Linux) einen Server beende, und neustarte (z.B Apache oder SVN), dann funktioniert das ja auch!? (und er läuft nicht über xinetd)

    Und unter Windows scheint es ohnhin immer zu funktionieren.

    lagalopex schrieb:

    Und für gewöhlich braucht man ein Programm (mit bind&listen) auch nicht beenden und gleich neustarten. (Außer vielleicht in der Entwicklung.)

    Das ist natürlich richitg, aber ich dachte es sei ein Fehler in meinem Programm beim Freigeben der Sockets.

    Grüße
    Mattes


Anmelden zum Antworten