CSocket mit threads, mehrere Clients

thekiller

Viceinator
Hallo,

ich möchte mir eine eigene Klasse schreiben, die den gesamten Netzwerkbereich meines Programmes abdeckt.
Bisher habe ich die Klasse so weit, dass die Verbindung von einem Client akzeptiert werden kann, funktioniert auch alles wunderbar.
Dann wollte ich sie so erweitern, dass mehrere Clients mit dem Server verbinden können. Ich habe die Variante mit Threads versucht, da ich die eh verwenden wollte in meinem Programm. Mein Problem ist nun, dass mein Programm beim 2. aufruf von Accept immer abstürzt. Nehme an ich hab irgendwo in der ganzen Sache etwas falsch verstanden.
Kann mir vielleicht jemand mal genau erklären wie genau es aussehen muss, damit ein 2. Client verbinden kann? Wie muss der Aufruf von accept aussehen? Wie wähle ich den Client aus zu dem später Daten gesendet oder empfangen werden usw.
Wäre echt genial! Danke schonmal im Voraus!

So habe ich es bisher gemacht:
1. Instanz meiner Klasse Global deklariert
2. In Haupt-Thread Server Socket erstellt über Methode meiner Klasse
3. In Haupt-Thread Methode aufrufen die mit Accept wartet
4. Wenn Accept erfolgreich war neuen Thread starten
5. Im neuen Thread 2. mal Methode aufrufen die mit Accept wartet <- ABSTURZ

Ach was solls, hier mal das wichtigste aus meinem Source. Aber nicht wundern warum kaum Sicherheitsabfragen drin sind, is ja erstmal alles noch zum herstellen der eigentlichen Funktionalität und zum testen. Daher auch printf-debuggen^^

MFCMain.h(MFCMain wird in _tmain aufgerufen, also Win32-Console-App mit MFC):
C++:
#ifndef	_MFCMAIN_H
#define	_MFCMAIN_H

#include "MFCSocket.h"

using namespace std;

BOOL	MFCMain();

#endif

MFCMain.cpp
C++:
#include "stdafx.h"
#include "MFCMain.h"

UINT	ThreadProc(LPVOID pParam) {
	cout << "Das hier is der Thread" << endl;

	cout << "Thread: Warte auf verbindung vom Client" << endl;
	if(MFCSocketServer.WaitForNewClient() == 0) { // Absturz
		cout << "Thread: Anzahl maximaler Clients wurde schon erreicht" << endl;
		system("PAUSE");
		return 0;
	}
	cout << "Thread: Client hat verbunden" << endl;

	return 0;
}

BOOL	MFCMain() {
	// Deklaration
	CWinThread	*Thread;
	UINT		uiPort;

	// Initialisierung Server
	uiPort	= 50000;
	cout << "Server starten auf Port: " << uiPort << endl;
	MFCSocketServer.OpenServer(uiPort);

	cout << "Warte auf verbindung vom Client" << endl;
	if(MFCSocketServer.WaitForNewClient() == 0) {
		cout << "Anzahl maximaler Clients wurde schon erreicht" << endl;
		system("PAUSE");
		return 0;
	}
	cout << "Client hat verbunden" << endl;

	Thread	= AfxBeginThread(ThreadProc, NULL);
	WaitForSingleObject(Thread, INFINITE);

	system("PAUSE");
	return true;
}

MFCSocket.h:
C++:
#ifndef _MFCSOCKET_H
#define	_MFCSOCKET_H

#include <afxsock.h>

class MFCSocket {
	private:
		// Deklaration
		CSocket	*pCSocketServer;
		CSocket	*pCSocketClient;

		UINT	uiMaxClients;
		UINT	uiNumClients;

		BOOL	isServer;
		BOOL	isOpen;

		UINT	uiPort;

	public:
		// Methoden
		MFCSocket();
		~MFCSocket();

		BOOL	OpenServer(UINT _uiPort = 50000);
		
		UINT	WaitForNewClient();
		
		VOID	Close();
};

extern	MFCSocket	MFCSocketServer;

#endif

MFCSocket.cpp:
C++:
#include "stdafx.h"
#include "MFCSocket.h"

// Methoden
MFCSocket::MFCSocket() {
	// Initialisierung
	AfxSocketInit();

	this->uiMaxClients	= 32;
	this->uiNumClients	= 0;

	this->isServer		= false;
	this->isOpen		= false;

	this->uiPort		= 50000;

	this->pCSocketServer		= new CSocket;
	this->pCSocketClient		= new CSocket[this->uiMaxClients];
}

MFCSocket::~MFCSocket() {
}

// Einen Server öffnen
BOOL	MFCSocket::OpenServer(UINT _uiPort) {
	// prüfen ob schon eine Server/Client Verbindung besteht
	if(this->isOpen) {
		// es besteht bereits eine Verbindung
		// ABBRUCH
		return false;
	}

	this->uiPort	= _uiPort;

	// Server erstellen
	this->pCSocketServer->Create(this->uiPort);
	this->pCSocketServer->Bind(this->uiPort);
	this->pCSocketServer->Listen();

	this->isOpen	= true;
	this->isServer	= true;

	return true;
}

UINT	MFCSocket::WaitForNewClient() {
	// Prüfe ob maximale Anzahl an Clients erreicht wurde
	if(this->uiNumClients >= this->uiMaxClients) {
		return 0;
	}

	// Warte auf Clientverbindung
	while(1) {
		if(this->pCSocketServer->Accept(this->pCSocketClient[this->uiNumClients])) {
			break;
		}
		Sleep(10);
	}
	
	//this->pCSocketSocket->Accept(*this->pCSocketConnection);
	this->uiNumClients++;

	return this->uiNumClients;
}

VOID	MFCSocket::Close() {

}

MFCSocket	MFCSocketServer;

Hoffe Ihr seht einigermaßen durch^^


LG Manuel
 
Zuletzt bearbeitet von einem Moderator:
Hi,

Code:
...
// Warte auf Clientverbindung    
while(1) {
        if(this->pCSocketServer->Accept(this->pCSocketClient[this->uiNumClients])) {
            break;
        }
        Sleep(10);
    }

EDIT:
Musst du hier nicht einen Zeiger auf ein CSocket übergeben?

Achja...
Und wenn du mit einer Klasse arbeitest, dessen Instanz gleichzeitig von mehreren Threads benutzt wird, würde ich mir mal gedanken machen, ob du nicht ein bisschen was einbauen willst, was das ganze ThreadSave macht.

Hab schon lange nichts mehr mit MFC gemacht deswegen weiß ich den genauen Ablauf nicht mehr, aber wo fängt das ganze an?
Bei "MFCMain()" ?

Gruß
Anfänger
 
Zuletzt bearbeitet:
An der Stelle die du angegeben hast übergebe ich einen Pointer an Accept, daran liegts also nicht. Mittlerweile habe ich auch schon rausbekommen an was es scheiterte. Man muss wohl für jeden Thread der CSockets verwendet AfxSocketInit() aufrufen, dann gehts. Keine Ahnung warum es geht, weil man AfxSocketInit() ja eigentlich nur einmal aufrufen sollte, aber es geht...
Naja und was die Threadsicherheit angeht, die kommt ja noch. Is ja schließlich noch alles in Arbeit^^

LG Manuel
 
Hi,

kann es sein das die Funktion in jedem Thread einmal aufgerufen werden muss?
Dann wäre das ganze ja noch logisch ;)

Gruß
Anfänger
 
An der Stelle die du angegeben hast übergebe ich einen Pointer an Accept, daran liegts also nicht. Mittlerweile habe ich auch schon rausbekommen an was es scheiterte. Man muss wohl für jeden Thread der CSockets verwendet AfxSocketInit() aufrufen, dann gehts. Keine Ahnung warum es geht, weil man AfxSocketInit() ja eigentlich nur einmal aufrufen sollte, aber es geht...
Naja und was die Threadsicherheit angeht, die kommt ja noch. Is ja schließlich noch alles in Arbeit^^

LG Manuel

Ähm hast du überhaupt Ahnung womit du da arbeitest?^^
Also zum Thema Threadsicherheit das ganze ist alles schon in MFC implementiert, wie wäre es wenn du dich einfach mal schlau machen würdest was dir alles zur Verfügung steht (Dokumentation zu verwendeten Methoden Klassen...)!
Ansosnten könntest du gleich alles selber schreiben, was ich dir persöhnlich auch raten würde. Zumidest du grundlagen Win-Api Threads und standart Winodws Sockets un einfach mal eine Server Programmieren, welcher uendlich viele Clienten animmt und jeder Text schreiben kann, welcher an alle Clienten weitergesendet wird.
Dann würdest du auch verstehen was da im Hintergrund läuft und weißt, wie du alles machst udn was benötigt wird.

Ich Helfe gerne^^
MFG
 
Also ich erkläre mal die Grundlegendsten Schritte.
Allgeimein solltest du sehen, dass du in MFC fast alle Klassen ablieten musst/solltest kommt ganz darauf an.

So würde ich das hier auch machen!

1.
CWinThread Klasse ableiten. Ich nehm mal als Beispiel neuen Klassenname YourThreadClassName.
2.
Danach Implementierst du die InitInstance() Funktion // Was du benötigst z.B. Sockets erstellen... Ansonten auch leer
Die Klasse CWinThread ist abstrakt !
C++:
virtual BOOL InitInstance( );
4.
So nun würde ich persöhnlich eine std Liste von YourThreadClassName Objekten anlegen.
5.
Dann Threads erstellen mit je nachdem wie du deine Liste genannt hast:
C++:
MyList.Insert(AfxBeginThread(RUNTIME_CLASS(CYourThreadClassName)));
Wenn mich nicht alles Täuscht funktinierte das mit Insert musste nochmal nachschauen // ich rede von der std-List
6.
jetzt solltest du 2 Threads haben den einen der Clienten aktzeptiert und der ganz normale "Win-Api Haupthread" (ja sind auch mehrer).
.....

Ja und dann halt immer eien neuen Thread pro Client definieren^^ wie du das auch sicher vorhattest und dann Nachrichtenbehandlung (Der Client Messages)...

+ Socket Liste (wäre auch nicht schlecht)

ExitInstance() ist auch notwendig.

So für die Kommunikation zwischen den Threads liefert MFC:
PostThreadMessage() Mehtode
und um die Nachricht zu empfangen dann ON_THREAD_MESSAGE()
----->> Standart message map System.

und wenn du bei der Syncornisation bist meldest du dich am besten noch einmal.
bb
 
Zuletzt bearbeitet:
Hi,

also mit mehreren Clients zu arbeiten ist eigentlich gar nicht so schwer ;-) (Man muss nur wissen wie)

Am besten geht es eigentlich mit der Funktion select() und FD_SET. Hier mal ein Auszug daraus, dass ein Server-Programm mehrere Clients annehmen und bearbeiten kann:

Code:
#include <windows.h>

//erstmal alle Dateitypen die wir brauchen:

WSADATA WSAData;
SOCKET Socket_1;
SOCKET Socket_2;
sockaddr_in Server_Adresse;
FD_SET fdSet;
char *cIP_Adresse;
int iIP;
int iPortnummer = 1234;
int iErgebnis_Verbindung;
int iErgebnis_SendenEmpfangen;

//und hier der Programmcode:

    WSAStartup(MAKEWORD(2, 0), &WSAData);
    
    Socket_1 = socket(AF_INET, SOCK_STREAM, 0);
    
    if(Socket_1 == -1)
    {
        closesocket(Socket_1);
    }
    else
    {
        Server_Adresse.sin_family = AF_INET;
        Server_Adresse.sin_port = htons(iPortnummer);
        Server_Adresse.sin_addr.s_addr = htons(INADDR_ANY);
        
        iErgebnis_Verbindung = bind(Socket_1, (sockaddr*)&Server_Adresse, sizeof(sockaddr));
        
        if(iErgebnis_Verbindung == -1)
        {
            closesocket(Socket_1);
        }
        else
        {
            iErgebnis_Verbindung = listen(Socket_1, 0);
            
            if(iErgebnis_Verbindung == -1)
            {
                closesocket(Socket_1);
            }
            else
            {
                Socket_2 = INVALID_SOCKET;
                
                while(true)
                {
                    FD_ZERO(&fdSet);
                    FD_SET(Socket_1, &fdSet);
                    
                    if(Socket_2 != INVALID_SOCKET)
                    {
                        FD_SET(Socket_2, &fdSet);
                    }
                    
                    iErgebnis_Verbindung = select(0, &fdSet, NULL, NULL, NULL);
                    
                    if(iErgebnis_Verbindung == SOCKET_ERROR)
                    {
                        closesocket(Socket_1);
                        closesocket(Socket_2);
                    }
                    else
                    {
                        if(FD_ISSET(Socket_1, &fdSet))
                        {
                            if(Socket_2 == INVALID_SOCKET)
                            {
                                Socket_2 = accept(Socket_1, NULL, NULL);
                                
                                if(Socket_2 == -1)
                                {
                                    closesocket(Socket_2);
                                    Socket_2 = INVALID_SOCKET;
                                }
                                else
                                {
                                    //Hier kommst dann dein Code rein, der ausgeführt werden soll, wenn sich ein Client erfolgreich verbunden hat.
                                }
                            }
                            else
                            {
                                Sleep(10);  //und das ist dafür, dass du bei deiner CPU keine 100% auslastung bekommst
                            }
                        }
                    }
                }
            }
        }
    }

Hoffe es hilft dir...
 
Zuletzt bearbeitet:
:offtopic:
C++:
offtopic.begin()
Also ich erkläre mal die Grundlegendsten Schritte.
Allgeimein solltest du sehen, dass du in MFC fast alle Klassen ablieten musst/solltest kommt ganz darauf an.
...

Jaja;) der Cyber:) Hatten wir ja erst kürzlich das Thema (Server und Socket) nicht wahr:P

C++:
offtopic.return(success)
:P
 
Zuletzt bearbeitet von einem Moderator:
Zurück