Exception bei "Socket als Listener registrieren"

vfl_freak

Premium-User
[C/C++]Exception bei "Socket als Listener registrieren"

Hallo zusammen,
vielleicht kann mir jemand bei einem seltsamen Fehler helfen :)

Ich habe hier ein Server-Programm (C + C++ gemischt :-( mit Visual C++ 6.0 unter Win XP Prof.) übernommen, in dem im sog. "GenericServer" innerhalb eines Threads permanent eine Socketverbindung zum zugehörigen Client überwacht wird. Auf dieser Verbindung werden Befehle an dern Server gesendet und die Resulte zurückgeschickt!
Nun suche ich schon seit einiger Zeit nach der Ursache für gelegentliche, unregelmäßige Abstürze. Dabei ist mir im Debugger aufgefallen, dass jedesmal direkt nach dem Programmstart als erstes folgender Eintrag kommt:
"nicht abgefangene Ausnahme:" #GSWorkerServer.exe: 0x00000002: (kein Name)"
Ich habe die Stelle im Code jetzt soweit wie folgt einschränken können:

Code:
UINT __stdcall CGenericServer::AcceptThread(LPVOID pParam)
{
	CGenericServer *pGenericServer = (CGenericServer*)pParam;
	SOCKET s; // Main Listen Socket
	WORD wVersionRequested;
	WSADATA wsaData;
	sockaddr_in saLocal;
	WSAEVENT Handles[2];
	WSANETWORKEVENTS	NetworkEvents;
	sockaddr ClientAddr;
	INT addrlen = sizeof(ClientAddr);
	sockaddr_in sain;
	char cAddr[50];
	int result;
	
	saLocal.sin_family = AF_INET;
	saLocal.sin_port = htons(pGenericServer->m_ServerPort);
	saLocal.sin_addr.s_addr = INADDR_ANY;
	
	// Vorgabe für die Initialisierung der WinSockets zusammensetzen
	wVersionRequested = MAKEWORD(2, 2);
	
	// WinSockets initialisieren
	result = WSAStartup(wVersionRequested, &wsaData);
	if(result != 0)
	{
		TRACE("ERROR: WSAStartup(...) failure", "AcceptThread", result);
		return THREADEXIT_SUCCESS;
	}
	
	// aktuelle Version der Sockets auf Version 2 überprüfen
	if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
	{
		TRACE("ERROR: Requested Socket version not exist", "AcceptThread");
		pGenericServer->CleanupThread(NULL, NULL, NULL);
		return THREADEXIT_SUCCESS; 
	}
	
	// Socket erzeugen
	s = WSASocket(AF_INET, SOCK_STREAM, 0, (LPWSAPROTOCOL_INFO)NULL, 0, WSA_FLAG_OVERLAPPED);
	if(s == INVALID_SOCKET)
	{
		TRACE("ERROR: WSASocket(...) failure", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, NULL, NULL);
		return THREADEXIT_SUCCESS;
	}
	
	// Socket an die lokale Netzwerk-Adresse binden
	result = bind(s, (struct sockaddr *)&saLocal, sizeof(saLocal));
	if(result == SOCKET_ERROR)
	{
		pGenericServer->OnCleanupThread();
		TRACE("ERROR: bind(...) failure", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, NULL, s);
		return THREADEXIT_SUCCESS;
	}	

	// Socket als Listener registrieren
	try
	{
// ###########################
// Hier kommt es zur Exception 
// ###########################
		result = listen(s, SOMAXCONN); // SOMAXCONN=5 (Maximum queue length specifiable by listen)
	}
	catch(...)
	{
		TRACE("ERROR: listen(...) failure", "AcceptThread", WSAGetLastError());
	}

Leider durchblicke ich den Code auch nicht an allen Stellen zu 100%, habe ihn aber soweit wie hier dargestellt, kommentieren können :rolleyes: ..... leider ist hier bei uns sonst nix kommentiert oder dokumentiert :confused:
Gecatcht wird die Exception scheinbar leider auch nicht, d. h. das nachfolgende TRACE sehe ich nicht in der Konsole!! Allerdings läuft das Programm auch problemlos weiter (u. U. sogar mehrere Tage)!!

Hat irgendjemand schon mal ein gleichartiges Problem gehabt oder auch nur eine Idee, wie ich hier weiter vorgehen kann Bin leider mit meinem Latein ziemlich am Ende!

Danke und Gruß
Klaus
 
Zuletzt bearbeitet:
Guten Morgen,

so, nachdem ich im letzten Herbst endlich die Software von VS6 auf VS2010 umstellen konnte, habe ich jetzt nochmals versucht, mich dieses Problems anzunehmen .....

Leider hat sich grundlegend noch nichts geändert, lediglich die Meldung in der Konsole ist ein bisschen länger:
Eine Ausnahme (erste Chance) bei 0x7c812aeb in XXX_Server.exe: 0x00000002: Das System kann die angegebene Datei nicht finden.

Hier nochmals der relevante Codeausschnitt
C++:
    // Socket als Listener registrieren
    try
    {
        // ###########################
        // Bei diesem Aufruf kommt es zur Exception 
        // ###########################
        // SOMAXCONN hat jetzt den Wert 0x7fffffff - bedeutet lt. Hilfe :
       //  A value for the backlog of SOMAXCONN is a special constant that instructs the underlying service provider responsible for socket s to set the
       //  length of the queue of pending connections to a maximum reasonable value.
        result = listen(s, SOMAXCONN); 
    }
    catch(...)
    {
        TRACE("ERROR: listen(...) failure", "AcceptThread", WSAGetLastError());
    }

Die int-variable result hat anschließend stets den Wert "0" und es wird auch keine Exception gefangen (sprich: TRACE wird nie ausgegeben)!

Mir ist allerdings auch nach wie vor unklar, wo welcher 'Datei' hier bloß die Rede ist!

Inwieweit das ganze wirklich ein Problem darstellt, kann ich nicht sagen. Wir haben mit diesem Serverprogramm zwar gelegentlich irgendwann Probleme mit unerklärlichen Abstürzen, ich kann aber nicht sagen, ob es wirklich hiermit zusammenhängen kann ....

Ich hoffe inbrünstig, dass irgendwer hierzu eine Idee hat !

Gruß
Klaus
 
Zuletzt bearbeitet von einem Moderator:
Hallo,
ich bin zwar nur ein Laie auf dem Socket-Gebiet, aber das Listen bekommt ja als ersten Parameter den Socket File-Descriptor. Ist es möglich, dass dieser aus irgendwelchen Gründen nicht existiert? Auch wenn ich vermute, dass du dies bereits in einer anderen Exception auswertest, würde das zumindest die Fehlermeldung logisch machen.

Grüße Jennesta
 
Moin,

erst mal Danke für Deine Antwort !

aber das Listen bekommt ja als ersten Parameter den Socket File-Descriptor. Ist es möglich, dass dieser aus irgendwelchen Gründen nicht existiert?
nein, die existieren! Das haben wir geprüft!

Und da die Funktion ohne Error zurückkommt, denke ich auch nicht, dass es wirklich ein Problem ist.
Ich lebe damit jetzt schon seit 5 Jahren, seit ich dieses Projekt übernommen habe ...

Gruß
Klaus
 
Mit Exceptions hat listen erstmal (gottseidank) gar nichts zu tun, try/catch kannst du dir da also sparen.
Ich vermute das Problem eher bei den Threading-Geschichten. Prüfe alle Zugriffe auf Variablen, die sowohl von innerhalb als auch von außerhalb benutzt werden.

Wird denn mit dem Ergebnis von listen weitergearbeitet? Die Funktion ist doch da noch nicht zu Ende.
 
Moin,

Mit Exceptions hat listen erstmal (gottseidank) gar nichts zu tun, try/catch kannst du dir da also sparen.
Danke - das ist doch erstmal 'ne Aussage :)

Ich vermute das Problem eher bei den Threading-Geschichten. Prüfe alle Zugriffe auf Variablen, die sowohl von innerhalb als auch von außerhalb benutzt werden.
Ich habe schön desöfteren vesucht, diesen Bereich zu debuggen, was allerdings (nicht zuletzt aufgrund der Vielzahl der Verbindungen) nicht so ganz einfach ist. Dieser Accept-Thread ist IMHO ja 'nur' dazu da, um die Socket zu etablieren. Die eigentlich Datenverarbeitung findet dann in Client-Thread statt.

Wird denn mit dem Ergebnis von listen weitergearbeitet? Die Funktion ist doch da noch nicht zu Ende.
Na ja, eigentlich nur in soweit, dass ggf. abgebrochen würde, wenn result einen Socket-Error bringen würde, was aber in den letzten fünf Jahren noch nicht vor kam :) Von daher gehe ich auch mal davon aus, dass diese Meldung eigentlich nicht so wirklich von Bedeutung ist ...

Hier gerne mal der komplette Code der Funktion :
C++:
UINT __stdcall CGenericServer::AcceptThread(LPVOID pParam)
{
	CGenericServer *pGenericServer = (CGenericServer*)pParam;
	SOCKET s; // Main Listen Socket
	WORD wVersionRequested;
	WSADATA wsaData;
	sockaddr_in saLocal;
	WSAEVENT Handles[2];
	WSANETWORKEVENTS	NetworkEvents;
	sockaddr ClientAddr;
	INT addrlen = sizeof(ClientAddr);
	sockaddr_in sain;
	char cAddr[50];
	int result;
	
	saLocal.sin_family = AF_INET;
	saLocal.sin_port = htons(pGenericServer->m_ServerPort);
	saLocal.sin_addr.s_addr = INADDR_ANY;
	
	// Vorgabe für die Initialisierung der WinSockets zusammensetzen
	wVersionRequested = MAKEWORD(2, 2);
	
	// WinSockets initialisieren
	result = WSAStartup(wVersionRequested, &wsaData);
	if(result != 0)
	{
		pGenericServer->LogMessage(LOGFILENAME, "WSAStartup(...) failure", "AcceptThread", result);
		return THREADEXIT_SUCCESS;
	}
	
	// aktuelle Version der Sockets auf Version 2 überprüfen
	if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
	{
		pGenericServer->LogMessage(LOGFILENAME, "Requested Socket version not exist", "AcceptThread");
		pGenericServer->CleanupThread(NULL, NULL, NULL);
		return THREADEXIT_SUCCESS; 
	}
	
	// Socket erzeugen
	s = WSASocket(AF_INET, SOCK_STREAM, 0, (LPWSAPROTOCOL_INFO)NULL, 0, WSA_FLAG_OVERLAPPED);
	if(s == INVALID_SOCKET)
	{
		pGenericServer->LogMessage(LOGFILENAME, "WSASocket(...) failure", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, NULL, NULL);
		return THREADEXIT_SUCCESS;
	}
	
	// Socket an die lokale Netzwerk-Adresse binden
	result = ::bind(s, (struct sockaddr *)&saLocal, sizeof(saLocal));
	if(result == SOCKET_ERROR)
	{
		pGenericServer->LogMessage(LOGFILENAME, "bind(...) failure", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, NULL, s);
		return THREADEXIT_SUCCESS;
	}	

	// Socket als Listener registrieren
// ###################################################################################	
// Hier die nicht abgefangene Ausnahme in #XXXServer.exe: 0x00000002: (kein Name)
// ###################################################################################	
	result = listen( s, SOMAXCONN );
	if(result == SOCKET_ERROR)
	{
		pGenericServer->LogMessage(LOGFILENAME, "listen(...) failure", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, NULL, s);
		return THREADEXIT_SUCCESS;
	}	
	
	// ShutDownEvent erzeugen
 	pGenericServer->m_ShutdownEvent = WSACreateEvent();
	if(pGenericServer->m_ShutdownEvent == WSA_INVALID_EVENT)
	{
		pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for ShutdownEvent", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, NULL, NULL, s);
		return THREADEXIT_SUCCESS;
	}		

	// Event erzeugen
	WSAEVENT Event = WSACreateEvent();
	if(Event == WSA_INVALID_EVENT)
	{
		pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for Event", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(NULL, pGenericServer->m_ShutdownEvent, s);
		return THREADEXIT_SUCCESS;
	}		

	Handles[0] = pGenericServer->m_ShutdownEvent;
	Handles[1] = Event;

    // Event registrieren, der einkommende Verbindungen anzeigt
	result = WSAEventSelect(s, Event, FD_ACCEPT);
	if(result == SOCKET_ERROR)
	{
		pGenericServer->LogMessage(LOGFILENAME, "WSAEventSelect(...) failure", "AcceptThread", WSAGetLastError());
		pGenericServer->CleanupThread(Event, pGenericServer->m_ShutdownEvent, s);
		return THREADEXIT_SUCCESS;
	}


	// Aktiviert den Event	
	SetEvent(pGenericServer->m_ThreadLaunchedEvent);
	
	// Auf einkommende Verbindungen warten
	for(;;)
	{
		// Wartet auf das auftreten Shutdown- oder ankommende Verbindung
		DWORD EventCaused = WSAWaitForMultipleEvents(2,	Handles, FALSE, WSA_INFINITE, FALSE);
		if(EventCaused == WAIT_FAILED || EventCaused == WAIT_OBJECT_0)
		{
			if(EventCaused == WAIT_FAILED)
			{
				pGenericServer->LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) failure WAIT_FAILED", "AcceptThread", GetLastError());
			}
			else
			{
				pGenericServer->LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) failure WAIT_OBJECT_0", "AcceptThread", GetLastError());
			}
			pGenericServer->CleanupThread(Event, pGenericServer->m_ShutdownEvent, s);
			return THREADEXIT_SUCCESS;
		}

		result = WSAEnumNetworkEvents(s, Event, &NetworkEvents);
		
		// Ist ein Fehler aufgetreten
		if(result == SOCKET_ERROR)						 
		{
			pGenericServer->LogMessage(LOGFILENAME, "WSAEnumNetworkEvents(...) failure", "AcceptThread", WSAGetLastError());
			pGenericServer->CleanupThread(Event, pGenericServer->m_ShutdownEvent, s);
			return THREADEXIT_SUCCESS;
		}

		// ein neue Verbindung
		if(NetworkEvents.lNetworkEvents == FD_ACCEPT)
		{
			SOCKET ClientSocket = WSAAccept(s, &ClientAddr, &addrlen, NULL, NULL);
			memcpy( &sain, &ClientAddr, addrlen );
			sprintf_s(cAddr, sizeof cAddr, "%d.%d.%d.%d", sain.sin_addr.S_un.S_un_b.s_b1, sain.sin_addr.S_un.S_un_b.s_b2, sain.sin_addr.S_un.S_un_b.s_b3, sain.sin_addr.S_un.S_un_b.s_b4);

			// konnte Socket für einkommende Verbindung angelegt weden.
 			if(INVALID_SOCKET == ClientSocket)
			{
				pGenericServer->LogMessage(LOGFILENAME, "WSAAccept(...) failure", "AcceptThread", WSAGetLastError());
				continue; 
			}
			else
			{
				// einkommende Verbindung als neuer Client registrieren
				if(!pGenericServer->AddClient(ClientSocket, cAddr, sain.sin_port))
				{
					pGenericServer->LogMessage(LOGFILENAME, "AddClient(...) failure", "AcceptThread");
					continue; 
				}
			}
		}
	}

	// zum Abschluss aufräumen
	pGenericServer->CleanupThread(Event, pGenericServer->m_ShutdownEvent, s);
	pGenericServer->LogMessage(LOGFILENAME, "WSASocket(...) done...", "AcceptThread", WSAGetLastError());
	return THREADEXIT_SUCCESS; 
}

Gruß
Klaus
 
Zuletzt bearbeitet von einem Moderator:
Du könntest mal im VS-Debugger starten und mit einem Batch-File einfach eine große Anzahl an Client-Prozessen erzeugen, die dann an den Server connecten.

Die Meldung

Eine Ausnahme (erste Chance) bei 0x7c812aeb in XXX_Server.exe: 0x00000002: Das System kann die angegebene Datei nicht finden.

deutet darauf hin, das dein Projekt nicht mit Debugging-Symbolen kompiliert wurde. Sonst würde anstatt der Adresse doch wohl eher die Zeile und Datei an dieser Stelle stehen.

"Das System kann die angegebene Datei nicht finden" könnte darauf hindeuten, das ein IO-Fehler aufgetreten ist (evtl. eine Client-Verbindung die sofort wieder abgebrochen wurde, wegen Firewall-Regel, Viren-Scanner oder dergleichen).
 
Hi.

Zuerst einmal sind "first-chance exceptions" meist zu vernachlässigen.

Wenn du allerdings wissen willst wo die Exception ausgelöst wird, dann nutze einen Debug-Build und möglichst Debug-Builds der abhängigen Bibliotheken (MSCRT etc.).

Dann aktiviere die Unterbrechnung für first-chance Exceptions im Debugger (VS Menü "debug -> Exceptions", C/C++ Exceptions, WinAPI Exceptions usw.) und lass das Programm im Debugger laufen.

Um Laufzeitfehler zu untersuchen, nutze einen Debug-Build oder einen Release-Build mit Debugging-Informationen (.pdb Dateien). Entweder das Programm direkt im Debugger (z.B. WinDbg) ausführen oder einen JIT Debugger konfigurieren.

Gruß
 
Moin,

Zuerst einmal sind "first-chance exceptions" meist zu vernachlässigen
Danke, das beruhigt mich ja schon mal ein wenig ;-)

Wenn du allerdings wissen willst wo die Exception ausgelöst wird, dann nutze einen Debug-Build und möglichst Debug-Builds der abhängigen Bibliotheken (MSCRT etc.).
Dann aktiviere die Unterbrechnung für first-chance Exceptions im Debugger (VS Menü "debug -> Exceptions", C/C++ Exceptions, WinAPI Exceptions usw.) und lass das Programm im Debugger laufen.
Um Laufzeitfehler zu untersuchen, nutze einen Debug-Build oder einen Release-Build mit Debugging-Informationen (.pdb Dateien). Entweder das Programm direkt im Debugger (z.B. WinDbg) ausführen oder einen JIT Debugger konfigurieren.
Ah, ok. Das werde ich in den nächsten Tagen mal versuchen (muss mich derzeit um ein anderes dringendes Problem kümmern) !

Danke und Gruß
Klaus
 
Moin deepthroat,

Wenn du allerdings wissen willst wo die Exception ausgelöst wird, dann nutze einen Debug-Build und möglichst Debug-Builds der abhängigen Bibliotheken (MSCRT etc.).
Wie komme ich denn an die Debug-Builds der Bibliotheken ( das wäre dann MSCRTd?) ?

Dann aktiviere die Unterbrechnung für first-chance Exceptions im Debugger (VS Menü "debug -> Exceptions", C/C++ Exceptions, WinAPI Exceptions usw.) und lass das Programm im Debugger laufen.
hmm, leider kann ich nirgendwo eine Aktivierung hierfür finden (bspw. gibt es bei mir in "C++ Exceptions" nur "_com_error", "ATL::CATLException", "CException", "std:exception" und "void" - bei VS2010 V10.0.40219.1 SP1Rel) .....

Gruß
Klaus
 
Zurück