C
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.