WinSock-Server(select) mit CriticalSection ??



  • hi leute,

    habe mir ein TCP-Echo-Client-Server-Programm geschrieben und der Server kann mit select mehrere Clients handeln.
    Die Frage ist nun, wie ich prüfen kann, ob noch Clients mit der Server verbunden sind.

    Ich überlege in die Richtung Threads und CriticalSection, weiß bloß nicht genau, wie ich das genau implementieren soll.

    Im Hauptthread läuft der eigentliche Server und in einem zweiten Thread wird dauernd eine Variable geprüft, ob diese gleich null ist und wenn wenn ja dann wird eine Meldung angezeigt, dass keine Clients mehr verbunden sind ??
    (Die Variable soll = 0 sein, wenn alle Clients die Verbindung geschlossen haben !)
    Im Hauptthread wird die Variable immer um 1 erhöht, wenn ein neuer Client hinzugekommen ist.

    Is das die richtige Denkweise oder habt ihr noch andere Vorschläge ??
    Wenn ja, kann mir jemand dazu einen Tipp geben, wie man das realisieren kann ?

    thx im Voraus
    mfg R3dNeXX



  • zurückgezogen ... hmmm ... oder doch eine Möglichkeit dazu

    An irgend welchen Zählern herumpollern ist nicht so das Wahre, vor allem nicht in Zusammenhang mit Threads! Threads are devils!!!

    Nimm Semaphore!

    Die ist insbesondere für die Threadsynchronisation gebaut und vor allem: Sie verwaltet einen threadsafe Zähler.
    Genug geschwafelt, wie funkt sowas?

    Fange mal ab listen() an:

    if(listen(s, MAXCLIENTS) !=0){
        closesocket(s);
        WSACleanup();
        std::cout << "Kann nicht in den listen-Modus schalten" << std::endl;
        return 1;
    }
    
    //Create Semaphore
    HANDLE hSemaphore;
    LONG cMax = MAXCLIENTS;
    SECURITY_ATTRIBUTES sa = { 0 };
    sa.nLength=sizeof(sa);
    sa.bInheritHandle=true; //Threads müssen den Handle erben dürfen
    hSemaphore = CreateSemaphore(&sa, cMax, cMax, "MyThreadSemaphore32");
    

    MAXCLIENTS steht momentan auf den Wert: SOMAXCONN. Ich erzeuge eine Semaphore, die als Startwert also genau den Wert annimmt, die listen( ... ) noch durchlässt. Security_Attributes wird initialisiert und vor allem der bInherithandle mit true, damit Threads den Handle hSemaphore erben können. Sie müssen die Semaphore ja incrementieren, wenn sie ihr digitales Dasein beenden.

    Nach accept( ... ) wird ein entsprechender Thread für die Connection gestartet.

    DWORD dwWaitResult; 
    
    sAccept=accept(s, (SOCKADDR*)&addr, &nLen);
    if(sAccept != INVALID_SOCKET){
           dwWaitResult = WaitForSingleObject(hSemaphore, 0L);
           if(dwWaitResult == WAIT_TIMEOUT){
                send(sAccept, "ERROR: SERVER FULL", 18, 0);
                break;
           }	
           CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AcceptThread, (void*)sAccept, 0, &nID);
    }
    

    Mit WaitForSingleObject wird die Semaphore decrementiert. Dann der Thread, sofern kein Fehler wie leere Semaphore! auftritt.

    Der Thread erbt zunächst den Handle:

    HANDLE hSem = OpenSemaphore(SEMAPHORE_MODIFY_STATE, true, "MyThreadSemaphore32");

    Dann incrementiert er beim Abgang die Semaphore:

    if(!ReleaseSemaphore(hS, 1, NULL)){
    std::cout << "Ups, die Semaphore ist erschöpft, hier was tun!" << std::endl;
    }
    closesocket(s);

    Das Ganze mal als ein kleines Consolen-Proggie. Für Leute die das auch mal ausprobieren wollen: Ihr müsst im Linker noch die ws2_32.lib einbinden 😉
    (Hinweis für VC++6.0 User).

    #include <winsock2.h>
    #include <windows.h>
    #include <iostream>
    
    #define THEPORT 12355
    #define WAITEVENTTIMEOUT WSA_INFINITE
    #define MAXCLIENTS SOMAXCONN
    
    void OnRead(SOCKET s){
    
    }
    
    void CleanWSAEvent(SOCKET s, WSAEVENT ev, HANDLE hM, HANDLE hS){
        WSAEventSelect(s, ev, 0);
    	WSACloseEvent(ev);
        ReleaseMutex(hM);
    	CloseHandle(hM);
    	if(!ReleaseSemaphore(hS, 1, NULL)){
    	   std::cout << " nCc: " << GetLastError() << std::endl;
    	}
    	closesocket(s);
    }
    
    DWORD WINAPI AcceptThread(SOCKET s){
        SOCKET mySocket = s;
        WSAEVENT wE[1] = {WSACreateEvent()};
    	WSANETWORKEVENTS wnE;
    	SOCKADDR_IN inaddr;
    
    	HANDLE hSem = OpenSemaphore(SEMAPHORE_MODIFY_STATE, true, "MyThreadSemaphore32");
    
    	int nLen = sizeof(inaddr);
    	getsockname(s, (SOCKADDR*)&inaddr, &nLen);
        HANDLE hMutex = OpenMutex(MUTEX_MODIFY_STATE, true,inet_ntoa(inaddr.sin_addr)); 
    
    	for(;;){
    		 ZeroMemory(&wnE, sizeof(wnE));
             if(WSAEventSelect(mySocket, wE[0], FD_READ | FD_CLOSE) == SOCKET_ERROR) return 1;
    		 DWORD rs = WSAWaitForMultipleEvents(1, wE, false, WAITEVENTTIMEOUT, false);
    		 if(rs == WSA_WAIT_FAILED){
    			 CleanWSAEvent(mySocket, wE[0], hMutex, hSem);
    			 return 1;
    		 }
    
             if(WSAEnumNetworkEvents(mySocket, wE[0], &wnE) == SOCKET_ERROR) {
    			 CleanWSAEvent(mySocket, wE[0], hMutex, hSem);
    			 return 1;
    		 }
    
    		 switch(wnE.lNetworkEvents){
    			case FD_READ :
    				OnRead(mySocket);
    				break;
    			case FD_CLOSE :
    				CleanWSAEvent(mySocket, wE[0], hMutex, hSem);
    				return 0;
    		 }
    		 WSAResetEvent(wE[0]);
    	}
    	CleanWSAEvent(mySocket, wE[0], hMutex, hSem);
    	return 0;
    }
    
    int main(){
    	//Socket-Startup
    	WSADATA ws = { 0 };
    	if(WSAStartup(MAKEWORD(2,0), &ws) != 0){
    		std::cout << "Winsockets können nicht initialisert werden" << std::endl;
    	}
        //Get a Socket
    	SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if(s == INVALID_SOCKET){
    		std::cout << "Socket schlug fehl" << std::endl;
    		return 1;
    	}
        //Bind a Port
    	SOCKADDR_IN IPAddr;
    	IPAddr.sin_family=AF_INET;
    	IPAddr.sin_port=htons(THEPORT);
    	IPAddr.sin_addr.s_addr= INADDR_ANY;
    	if(bind(s, (SOCKADDR*)&IPAddr, sizeof(IPAddr)) == SOCKET_ERROR){
    		closesocket(s);
    		WSACleanup();
    		std::cout << "Port: " << THEPORT << " kann nicht gebunden werden" << std::endl;
    		return 1;
    	}
    	//Listenning
    	if(listen(s, MAXCLIENTS) !=0){
    		closesocket(s);
    		WSACleanup();
    		std::cout << "Kann nicht in den listen-Modus schalten" << std::endl;
    	    return 1;
    	}
    	//Create Semaphore
    	HANDLE hSemaphore;
        LONG cMax = MAXCLIENTS;
    	SECURITY_ATTRIBUTES sa = { 0 };
    	sa.nLength=sizeof(sa);
    	sa.bInheritHandle=true; //Threads müssen den Handle erben dürfen
    	hSemaphore = CreateSemaphore(&sa, cMax, cMax, "MyThreadSemaphore32");
    
    	//Accept
    	SOCKET sAccept;
    	SOCKADDR_IN addr;
    	ULONG nID = 0;
    
    	for(;;){
    	   int nLen = sizeof(addr);
    	   sAccept=accept(s, (SOCKADDR*)&addr, &nLen);
    	   if(sAccept != INVALID_SOCKET){
    	        CreateMutex(&sa, true, inet_ntoa(addr.sin_addr));
    	        if(GetLastError() == ERROR_ALREADY_EXISTS){
    		        send(sAccept, "ERROR: SERVER FULL", 18, 0);
    				continue;
    			}
    	        WaitForSingleObject(hSemaphore, 0L);	
    			CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AcceptThread, (void*)sAccept, 0, &nID);
    	   }
    	}
        //Cleanup
    	CloseHandle(hSemaphore);
    	closesocket(s);
    	WSACleanup();
    
    	return 0;
    }
    

    Die MUTEX hier will lediglich verhindern, dass jemand mit der gleichen IP den Server mehrfach connected. Ob das aber Angriffe verhindert ... glaube kaum.
    Die CreateMutex muss natürlich dann aber auch vor der Semaphore stehen.
    Klar ist, dass die MUTEX schon dafür sorgt, dass jeder Thread einzigartig (unicat) sein wird.

    Wie gesagt: Es ist eine Möglichkeit.

    Die von mir geposteten Vorschläge
    sind machbare, insbesondere lauffähige
    Snippets. Sie beanspruchen
    keine wissenschaftlich korrekte Notation
    etc., nur um studentische Hinterbänkler
    und solche, die das mal waren, zu befriedigen.
    Sie werden daher i.d.R. kurz und überschaubar
    gehalten.


Log in to reply