Problem mit verkapselten Sockets



  • Ich bastele zur Zeit an einem kleinen Server (als Client benutze ich Telnet). Die angenommenen Verbindungen werden in einer std::list<Client> gespeichert, wobei ein Client den Socket und den Benutzernamen speichert und sich um das Schließen des Sockets kümmert. Den Socket, der Verbindungen annimmt, und die Clients werden über ein fd_set und select() verwaltet.
    Wenn ich jetzt aber eine Verbindung herstelle, führt jeder Aufruf von select() zu einem WSAENOTSOCK (der über eine get-Methode aus dem Client geholte Socket macht also Probleme).

    Kann jemand sagen, woran das liegt / was ich anders machen muss? Eigentlich ist ein Socket unter Windows doch nur ein unsigned int, warum macht das dann diesen Fehler? Wenn ich die Clients einfach nur als Socket-Array speichere, dann funktioniert alles..



  • Ohne die relevanten Code-Ausschnitte bräuchte ich hellseherische Fähigkeiten. 🙄



  • Äh, klar.

    Erstmal die Client-Klasse:

    class Client
    {
    private:
    	SOCKET m_sock;
    	std::string m_ip;
    	std::string m_name;
    
    public:
    	Client(SOCKET accsock); // ruft vor allem accept() auf, und wirft bei dessen Fehlschlag eine Exception
    	~Client() { closesocket(m_sock); }
    	SOCKET getSocket() { return m_sock; }
    };
    

    Und hier die relevanten Zeilen aus main():

    int main()
    {
    	WSockHandler ws; // Ruft im Konstruktor WSAStartup() auf, und im Destruktor WSACleanup()
    	SOCKET accepts = socket(AF_INET, SOCK_STREAM, 0);
    	SOCKADDR_IN addr;
    	memset(&addr, 0, sizeof(SOCKADDR_IN));
    	addr.sin_port = htons(23);
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = INADDR_ANY;
    	bind(accepts, (SOCKADDR *)&addr, sizeof(SOCKADDR_IN));
    	listen(accepts, 10);
    
    	timeval notime = {0,1};
    	std::list<Client> clients;
    	fd_set set;
    	while (!appdone())
    	{
    		FD_ZERO(&set);
    		FD_SET(accepts, &set);
    		for (std::list<Client>::iterator it = clients.begin(); it != clients.end(); ++it)
    		{
    			SOCKET s = it->getSocket();
    			if (s != INVALID_SOCKET)
    				FD_SET(s, &set);
    		}
    		if (select(0, &set, 0, 0, &notime) == SOCKET_ERROR)
    		{
    			std::cout << timestamp() << "Error: Error while select()ing! Error code: " << WSAGetLastError() << '\n';
    			continue;
    		}
    
    		if (FD_ISSET(accepts, &set))
    		{
    			try
    			{
    				clients.push_back(accepts);
    			}
    			catch (accept_failed) {}
    		}
    
    		/* Hier ist dann noch die restliche Abfrage des FD_SETs */
    	}
    }
    

    Die Überprüfungen der Socketfuntionen habe ich rausgenommen, die laufen aber einwandfrei.

    Das Problem ist wie gesagt der select()-Aufruf, sobald ich eine Verbindung aufbaue gibt es Fehlercodes zurück.



  • ich finde eine microsekunde ein bisschen kurz zum selecten 😃
    übergib mal statt &notime NULL (zum testen)



  • Ändert auch nichts. Eine Mikrosekunde habe ich aber auch schon in einem anderen Programm verwendet, da lief das problemlos..



  • Also erstmal folgendes:

    The parameter time-out controls how long the select can take to complete. If time-out is a null pointer, select will block indefinitely until at least one descriptor meets the specified criteria. Otherwise, time-out points to a TIMEVAL structure that specifies the maximum time that select should wait before returning. When select returns, the contents of the TIMEVAL structure are not altered. If TIMEVAL is initialized to {0, 0}, select will return immediately; this is used to poll the state of the selected sockets.

    timeval notime = {0,0};

    wäre also einfach bezeichnender ( nicht besser oder richtiger ).

    Ansonsten sieht soweit alles ok aus, deshalb würde ich vorschlagen, dass du mal den client-constructor postest.



  • Kannst du haben, macht aber nicht viel mehr als in dem Kommentar stand:

    Client::Client(SOCKET accsock)
    {
    	SOCKADDR_IN saddr;
    	int len = sizeof(SOCKADDR_IN);
    	m_sock = accept(accsock, (SOCKADDR *) &saddr, &len);
    	if (m_sock == INVALID_SOCKET)
    		throw accept_failed("Accept failed.");
    	m_ip = inet_ntoa(saddr.sin_addr);
    	// Loggen, Begrüßung senden
    }
    

    Und mit einem {0,0}-timeval war mir mal irgendwas komisches passiert, weiß nicht mehr was.



  • Lideric schrieb:

    [...]

    if (FD_ISSET(accepts, &set))
    		{
    			try
    			{
    				clients.push_back(accepts);
    			}
    			catch (accept_failed) {}
    		}
    
    		/* Hier ist dann noch die restliche Abfrage des FD_SETs */
    	}
    }
    

    [...]

    Ähmmm... Sieht irgendwie aus, als würde da ein Client-Objekt auf dem Stack erstellt ( clients.push_back(accepts) ), in dessen Constructor dann auch brav das accept erfolgt und m_sock auch brav initialisiert wird... dies wird dann an die Liste angehangen (kopiert), aber leider danach auch wieder zerstört ( closesocket() ). Damit ist die Socket dann geschlossen und das Client-Objekt in der Liste hat einen ungültigen Socket-Descriptor. Denk ich mir mal so.



  • Oh, verdammt, tatsächlich. Dann muss ich wohl ein Bisschen was am Design ändern. ..
    Aber auf jeden Fall vielen Dank!


Anmelden zum Antworten