<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Socket verschluckt Daten]]></title><description><![CDATA[<p>Hallo zusammen,</p>
<p>ich habe angefangen den Quellcode für den Netzwerkteil von dem Buch 3D-Spieleprogrammierung von Stefan Zerbst zu übernehmen. Allerdings habe ich es nicht 1:1 übernommen, sondern etwas abgeändert. Zum einem, verwende ich keine Windows-Events, weil ich den Code ggf später einmal auf Linux übernehmen möchte.<br />
Stattdessen habe ich FD_SETS benutzt um auf Ereignisse reagieren zu können.</p>
<p>Das Programm läuft auf dem eigenen Rechner ganz gut und relativ schnell. Wenn ich aber jetzt den Server auf einen anderen Rechner im Netzwerk starte, dann wird ein großer Teil der Daten &quot;verschluckt&quot;, wenn ich in einer Schleife ca. 1000 Datenpakete sende.</p>
<p>Wenn ich nun ein Sleep(1) in der Schleife einbaue, dann kommen mehr Pakete an, aber es dauert entsprechend lange. Ein kompletter Durchlauf ca 16 Sekunden.</p>
<p>Ich sitze nun 2 Tage an diesem Problem. Weder das Buch noch Google konnten mir bisher helfen. Vielleicht stelle ich auch einfach die falschen Fragen.</p>
<p>Ich poste hier mal den kompletten Code. Schonmal Entschuldigung, falls es zu lang sein sollte, aber ich kenne den Teil nicht, welcher verantwortlich ist für dieses Problem.</p>
<pre><code class="language-cpp">/////////////////////////////////////////////////////////////////////////////
// File      : CSocket.h
// Brief     : Testklasse fuer eine Socketimplementierung
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#ifndef _CSOCKET_H_
#define _CSOCKET_H_

//Includes und libs
#include &lt;winsock2.h&gt;
#include &lt;vector&gt;

#pragma comment (lib, &quot;Ws2_32.lib&quot;)

//Reservierte Nachrichtentypen
enum packagetype_e
{
	TYPE_NEWID,		//Neuer Client bekommt eine ID
	TYPE_NEWCLIENT, //Broadcast an Clients, dass sich ein Client angemeldet hat
	TYPE_REMCLIENT, //Broadcast an Clients, dass sich ein Client abgemeldet hat

	TYPE_CHAR = 50	//Chat Nachricht (Testweise)
};

//Datenpaket
typedef struct netpackage_type
{
	UINT nType;		  //Nachrichtentyp
	UINT nSender;	  //Absender (0 bedeutet Server)
	UINT nRecipient;  //Empfaenger (0 bedeutet Server, 1 bedeutet alle Clients)

	UINT nDataLength; //Datenlaenge von lpData in Bytes
	void *lpData;	  //Zu sendende Daten
} netpackage_t;

//Clients
typedef struct clients_type
{
	UINT	nID;		//ID des Clients
	SOCKET	nSocket;	//Socket vom Client
	char	szIP[256];	//IP-Adresse
	ULONG	nConnected;	//Timestamp der Verbindung
} client_t;

//Socket Klasse
class CSocket
{

private:
	SOCKET  m_nSocket;			//Socket
	UINT    m_nPort;			//Port
	UINT	m_nPackets;			//Anzahl der wartenden Pakete
	UINT	m_nRec;				//Anzahl der Receives
	UINT	m_nMaxSize;			//Maximale Sendegroesse inkl. Header
	FD_SET  m_FdSet;			//Socket Array (http://www.c-worker.ch/tuts/select.php)
	char   *m_cBuffer;//[65536];	//Empfangspuffer
	char   *m_lpSendBuffer;		//Sendepuffer

	//Clients
	std::vector&lt;client_t&gt; m_vClients;

	//Einen Client akzeptieren
	bool Accept();

	//Daten empfangen
	bool Receive(const client_t *lpClient);

	//Schreibe den letzen WSA Error
	void LogLastError(const char *szFunction);

public:
	CSocket();
	~CSocket();

	//Socket Initialisierung
	bool CreateSocket();

	//Socket an einen Port und eine Schnittstelle binden. Wird NULL uebergeben
	//so wird INADDR_ANY benutzt.
	bool BindSocket(UINT nPort, const char *szSocketAddress = NULL);

	//Serverfunktion aktivieren
	bool Listen();

	//Zu einem Server verbinden
	bool Connect(const char *szServer, UINT nServerPort);

	//Daten senden. Diese Funktion wird vom Client und von dem Server benutzt.
	//Der Server muss zusaetzlich den Socket angeben. Clients koennen diesen
	//Parameter ingnorieren.
	bool Send(const char *szData, UINT nSize, SOCKET nReceiver = 0);
	bool Send(const netpackage_t *lpPackage, SOCKET nReceiver = 0);

	bool Disconnect();

	bool IsConnected();

	//Anzahl der verbundenen Clients zureuckgeben
	int GetConnectedClients();

	//TEST
	bool Loop();

};

#endif //_CSOCKET_H_
</code></pre>
<pre><code>/////////////////////////////////////////////////////////////////////////////
// File      : CSocket.cpp
// Brief     : Testklasse fuer eine Socketimplementierung
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#include &quot;CSocket.h&quot;
#include &lt;iostream&gt;

CSocket::CSocket()
{
	m_nSocket  = INVALID_SOCKET;
	m_nPort    = 0;
	m_nPackets = 0;
	m_nRec	   = 0;
	m_nMaxSize = 65536;
	m_cBuffer  = NULL;

	m_lpSendBuffer = new char[m_nMaxSize];
}

CSocket::~CSocket()
{
	//Socket schliessen, falls dieser noch gueltig ist
	if (m_nSocket != INVALID_SOCKET)
		Disconnect();

	//Aufraeumen
	WSACleanup();

	delete [] m_lpSendBuffer;
}

//Einen Client akzeptieren
bool CSocket::Accept()
{
	sockaddr_in saClientAddress;
	int			nClientSize;
	client_t	client;

	//Client akzeptieren
	nClientSize= sizeof(sockaddr_in);
	client.nSocket = accept(m_nSocket, (SOCKADDR*)&amp;saClientAddress, &amp;nClientSize);

	//Erfolgreich ?
	if (client.nSocket == INVALID_SOCKET)
	{
		LogLastError(&quot;Accept&quot;);
		return false;
	}

	//Informationen abfragen
	SOCKADDR_IN sai; 

    int sai_len = sizeof( SOCKADDR_IN ); 
        memset( &amp;sai, 0, sizeof( SOCKADDR_IN ) ); 
        getpeername( client.nSocket, (SOCKADDR*)&amp;sai, &amp;sai_len ); 

    strcpy(client.szIP , inet_ntoa( sai.sin_addr ) ); 

	printf(&quot;New Client : %s\n&quot;, client.szIP);

	//Client hinzufuegen
	m_vClients.push_back(client);

	return true;
}

//Daten empfangen
bool CSocket::Receive(const client_t *lpClient)
{
	UINT nSize	= 65536;	//Maximale Bytes pro Durchlauf
	UINT nRead	= 0;		//Anzahl gelesene Bytes
	UINT nSeek	= 0;		//Position im Buffer
	UINT nLeft	= 0;		//Verbleibende Bytes
	bool bDone	= false;	//True, wenn fertig
	bool bFirst = true;		//True beim dem ersten Schleifendurchlauf

	m_cBuffer = new char[nSize];

	//Zeiger auf Netzwerkpaket
	netpackage_t *lpPackage = NULL;

	//Totale groesse des Paketes
	UINT nPackageSize		= 0;

	//Groesse eines Header
	UINT nDefPackageSize	= sizeof(netpackage_t);

	//Debug
	m_nRec++;
	printf(&quot;Empfange Daten (%d) von %s\n&quot;, m_nRec, lpClient-&gt;szIP);

	//Bei jedem Schleifendurchlauf nSize Bytes Daten lesen, bis
	//keine mehr vorhanden sind
	while (!bDone)
	{
		nRead = recv(lpClient-&gt;nSocket, &amp;m_cBuffer[nLeft], nSize - nLeft, 0);

		//Client weg ?
		if (bFirst)
		{
			if (nRead == 0)
				return false;

			bFirst = false;
		}

		//Fehler ?
		if (nRead == SOCKET_ERROR)
		{
			int nError = WSAGetLastError();

			//Kritischer Fehler ?
			if ((nError != WSAEMSGSIZE) &amp;&amp; (nError != WSAEWOULDBLOCK))
			{
				bDone = true;

				printf (&quot;\a\a\aAchtung Komischer Fehler !\n&quot;);
				return false;
			}
		}

		//Nun sind nRead Bytes im Buffer
		if (nRead &lt;= 0)
			bDone = true;
		else
		{
			//Alte Daten im Buffer beachten
			nRead += nLeft;

			//Loopen, bis wir einen kompletten Header finden
			while ((nRead - nSeek) &gt;= nDefPackageSize) // Ergaenzt : '='
			{
				//Naechstes Datenstueck
				lpPackage    = (netpackage_t*)&amp;m_cBuffer[nSeek];
				lpPackage-&gt;lpData = &amp;m_cBuffer[nSeek] + nDefPackageSize;
				nPackageSize = nDefPackageSize + lpPackage-&gt;nDataLength;

				//Haben wir das ganze Datenpaket empfangen ?
				if ((nRead - nSeek) &gt;= nPackageSize)
				{

					//TODO : Enqueue Message
					//printf(&quot;\r#%d    &quot;, m_nPackets);

					if (lpPackage-&gt;nType == TYPE_CHAR)
					{

						m_nPackets++;

						printf(&quot;ID : %d - # : %d) : %s\n&quot;, lpPackage-&gt;nSender, m_nPackets, lpPackage-&gt;lpData);
						std::cout &lt;&lt; m_cBuffer &lt;&lt; std::endl;
					}
					else
					{
						//m_nPackets--;

						printf(&quot;Paket #%d Fehlerhaft.\n&quot;, m_nPackets);
					}

					nSeek += nPackageSize;
				}
				else
				{
					//Das Bruchstueck an den Anfang des Buffers kopieren und weiterlesen
					memcpy(m_cBuffer, &amp;m_cBuffer[nSeek], nRead - nSeek);
					nLeft = nRead - nSeek;

					break;
				}
			}

			//Haben wir alle Daten ?
			if (nRead &lt;= nSize) //Ergaenzt : '='
				bDone = true;

		}
	}//while

	//printf(&quot;[%d] %s\n&quot;, m_nPackets, m_cBuffer);

	Sleep(1);

	delete [] m_cBuffer;

	return true;
}

//Schreibe den letzten WSA Error
void CSocket::LogLastError(const char *szFunction)
{
	UINT nLastError = WSAGetLastError();

	if (nLastError == 0)
		return;

	std::cout &lt;&lt; &quot;Socket Error : [&quot; &lt;&lt; szFunction &lt;&lt; &quot;] &quot;;

	switch (nLastError)
	{
		case WSANOTINITIALISED:
			std::cout &lt;&lt; &quot;Successful WSAStartup not yet performed.&quot; &lt;&lt; std::endl;
			break;

		case WSAEADDRNOTAVAIL:
			std::cout &lt;&lt; &quot;Cannot assign requested address. The requested address is not valid on this local computer.&quot; &lt;&lt; std::endl;
			break;

		case WSAHOST_NOT_FOUND:
			std::cout &lt;&lt; &quot;Host not found.&quot; &lt;&lt; std::endl;
			break;

		case WSAECONNREFUSED:
			std::cout &lt;&lt; &quot;Connection refused.&quot; &lt;&lt; std::endl;
			break;

		default:
			std::cout &lt;&lt; &quot;Unknown WSA Error #&quot; &lt;&lt; nLastError &lt;&lt; std::endl;
			break;
	}
}

//Socket erzeugen
bool CSocket::CreateSocket()
{
	WORD wVersionRequested;
    WSADATA wsaData;
	int nError;	

	//WSA Starten
	wVersionRequested = MAKEWORD(2, 2);
    nError = WSAStartup(wVersionRequested, &amp;wsaData);

	if (nError != 0)
	{
		LogLastError(&quot;CreateSocket() WSAStartup&quot;);
		return false;
	}

	//Existiert bereits ein Socket ?
	if (m_nSocket != INVALID_SOCKET)
		if (Disconnect() == false)
			return false;

	//Socket erstellen
	m_nSocket = socket(AF_INET, SOCK_STREAM, 0);

	//Erfolgreich ?
	if (m_nSocket == INVALID_SOCKET)
	{
		LogLastError(&quot;CreateSocket() socket(AF_INET, SOCK_STREAM, 0)&quot;);
		return false;
	}

	return true;
}

//Socket an einen Port und eine Schnittstelle binden. Wird NULL uebergeben
//so wird INADDR_ANY benutzt.
bool CSocket::BindSocket(UINT nPort, const char *szSocketAddress)
{
	sockaddr_in saServerAddress;

	memset(&amp;saServerAddress, 0, sizeof(sockaddr_in));

	saServerAddress.sin_family		= AF_INET;

	if (szSocketAddress != NULL)
		saServerAddress.sin_addr.s_addr = inet_addr(szSocketAddress);
	else
		saServerAddress.sin_addr.s_addr = INADDR_ANY;//inet_addr(&quot;127.0.0.1&quot;);

	saServerAddress.sin_port		= htons(nPort);

	//Socket binden
	if (bind(m_nSocket, (SOCKADDR*)&amp;saServerAddress, sizeof(saServerAddress)) == SOCKET_ERROR)
	{
		LogLastError(&quot;BindSocket()&quot;);
		Disconnect();

		return false;
	}

	return true;
}

//Socket in den Listen-Modus setzen
bool CSocket::Listen()
{
	if (listen(m_nSocket, 64) != 0)
	{
		LogLastError(&quot;Listen()&quot;);
		return false;
	}

	return true;
}

//Zu einem Server verbinden
bool CSocket::Connect(const char *szServer, UINT nServerPort)
{
	sockaddr_in saServerAddress;
	LPHOSTENT	lpHost = NULL;

	//Es wird versucht den Server zu finden
	memset(&amp;saServerAddress, 0, sizeof(sockaddr_in));

	saServerAddress.sin_port = htons(nServerPort);
	saServerAddress.sin_family = AF_INET;
	saServerAddress.sin_addr.S_un.S_addr = inet_addr(szServer);

	//Falls ein Name und keine IP uebergeben wurde, wird versucht
	//diesen aufzuloesen.
	if (saServerAddress.sin_addr.S_un.S_addr == INADDR_NONE)
	{
		lpHost = gethostbyname(szServer);

		if (lpHost != NULL)
			saServerAddress.sin_addr.S_un.S_addr = ((LPIN_ADDR)lpHost-&gt;h_addr)-&gt;S_un.S_addr;
		else
		{
			LogLastError(&quot;gethostbyname()&quot;);
			return false;
		}

	}

	//Zum Server verbinden
	if (connect(m_nSocket, (SOCKADDR*)&amp;saServerAddress, sizeof(sockaddr)) == SOCKET_ERROR)
	{
		LogLastError(&quot;connect()&quot;);

		Disconnect();
		return false;
	}

	//TODO : Erfolgreichen Verbindungsstatus setzen

	return true;
}

//Daten senden. Diese Funktion wird vom Client und von dem Server benutzt.
//Der Server muss zusaetzlich den Socket angeben. Clients koennen diesen
//Parameter ingnorieren.
bool CSocket::Send(const char *szData, UINT nSize, SOCKET nReceiver)
{
	UINT nSend = 0;
	UINT nLeft = 0;

	if (nReceiver == 0)
		nReceiver = m_nSocket;

	while (nSend &lt; nSize)
	{
		nLeft = send(nReceiver, szData + nSend, nSize - nSend, 0);

		//Erfolgreich ?
		if (nLeft == SOCKET_ERROR)
		{
			LogLastError(&quot;send()&quot;);

			return false;
		}

		if (nLeft == nSize)
			break;

		printf(&quot;nLeft = %d\n&quot;, nLeft);

		nSend += nLeft;
	}

	return true;
}

bool CSocket::Send(const netpackage_t *lpPackage, SOCKET nReceiver)
{
	UINT nSizeTotal = sizeof(netpackage_t) + lpPackage-&gt;nDataLength;
	//char *lpBuffer = new char[m_nMaxSize]; //TODO : Eventuell als Member ?

	//Daten-Serialisierung
	memcpy(m_lpSendBuffer, lpPackage, sizeof(netpackage_t));
	memcpy(m_lpSendBuffer + sizeof(netpackage_t), lpPackage-&gt;lpData, lpPackage-&gt;nDataLength);

	//Daten Senden
	Send(m_lpSendBuffer, nSizeTotal, nReceiver);

	return true;
}

//Socket herunterfahren und schliessen
bool CSocket::Disconnect()
{
	if (m_nSocket == INVALID_SOCKET)
		return true;

	//Socket runterfahren
	shutdown(m_nSocket, SD_BOTH);

	//Socket schliessen
	closesocket(m_nSocket);

	m_nSocket = INVALID_SOCKET;

	return true;
}

//Anzahl der verbundenen Clients zureuckgeben
int CSocket::GetConnectedClients()
{
	return m_vClients.size();
}

//TEST
bool CSocket::Loop()
{
	int nSocketState;

	// Inhalt vom FD_SET leeren
	FD_ZERO(&amp;m_FdSet); 

	//Den Accept-Socket hinzufuegen
	FD_SET(m_nSocket, &amp;m_FdSet); 

	//Hier werden alle gueltigen Client Sockets dem FD_SET hinzugefuegt werden
	for (std::vector&lt;client_t&gt;::iterator it = m_vClients.begin(); it != m_vClients.end(); ++it)
	{
		//std::cout &lt;&lt; &quot;Client im Vector : &quot; &lt;&lt; it-&gt;szIP &lt;&lt; std::endl;

		if (it-&gt;nSocket != INVALID_SOCKET)
			FD_SET(it-&gt;nSocket, &amp;m_FdSet);
	}

	//Nun wird geschaut, welche Sockets Daten bereitstellen (oder accept() beim Server Socket)
	//Alle Clients, welche keine Daten haben, werden aus diesem Array entfernt.
	nSocketState = select(0, &amp;m_FdSet, NULL, NULL, NULL);

	//Erfolgreich ?
	if (nSocketState == INVALID_SOCKET)
	{
		LogLastError(&quot;select()&quot;);
		return false;
	}

	//Wird auf eine neue Verbindung gewartet ?
	if(FD_ISSET(m_nSocket, &amp;m_FdSet)) 
		Accept();

	//Die Clients werden durchlaufen und mit den empfangsbereiten Sockets verglichen.
	//Bei einer uebereinstimmung werden die Daten empfangen
	for (std::vector&lt;client_t&gt;::iterator it = m_vClients.begin(); it != m_vClients.end(); ++it)
		if (FD_ISSET(it-&gt;nSocket, &amp;m_FdSet))
		{
			if (!Receive(&amp;*(it)))
			{
				printf(&quot;Entferne Client.\n&quot;); //TODO : Wenn false, client entfernen und socket schliessen

				//Test
				m_vClients.erase(it);
				break;
			}
		}

	return false;
}
</code></pre>
<pre><code class="language-cpp">/////////////////////////////////////////////////////////////////////////////
// File      : main.h
// Brief     :
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#ifndef _MAIN_H_
#define _MAIN_H_

#include &quot;CSocket.h&quot;

//Prototypen
bool ConnectToServer(const char *szServer, UINT nPort);
bool CreateServer(UINT nPort);

#endif //_MAIN_H_
</code></pre>
<pre><code class="language-cpp">/////////////////////////////////////////////////////////////////////////////
// File      : main.cpp
// Brief     :
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#include &quot;main.h&quot;
#include &lt;iostream&gt;

using namespace std;

int main(int argc, char *argv)
{
	char cChoice = 0;

	cout &lt;&lt; &quot;Socket Testprogramm&quot; &lt;&lt; endl
		 &lt;&lt; &quot;================================================&quot; &lt;&lt; endl &lt;&lt; endl
		 &lt;&lt; &quot;Moechten Sie einen Server (s) oder einen Client (c) starten ? (q) fuer Quit :&quot; &lt;&lt; endl &lt;&lt; endl;

	do
	{
		cout &lt;&lt; &quot;Auswahl : &quot;;
		cin &gt;&gt; cChoice;
		cin.clear();
		cin.sync();

		if (cChoice == 's')
		{
			CreateServer(42202);

			return 0;
		}

		if (cChoice == 'c')
		{
			ConnectToServer(&quot;127.0.0.1&quot;, 42202);

			return 0;
		}

	} while(cChoice != 'q');

	return 0;
}

bool ConnectToServer(const char *szServer, UINT nPort)
{
	CSocket Socket;

	//CreateSocket()
	cout &lt;&lt; &quot;Erstelle Socket ... &quot;;

	if (Socket.CreateSocket())
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
	{
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

		return false;
	}

	//Verbinde zum Server
	//CreateSocket()
	cout &lt;&lt; &quot;Verbinde zum Server [&quot; &lt;&lt; szServer &lt;&lt; &quot;:&quot; &lt;&lt; nPort &lt;&lt; &quot;] ... &quot;;

	if (Socket.Connect(szServer, nPort))
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
	{
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

		return false;
	}

	//Testpaket
	netpackage_t package;

	char szMessage[256] = &quot;Hallo Welt, wie geht es Dir denn heute so :)&quot;;
	package.lpData = new char[1024];

	package.nType		= TYPE_CHAR;
	package.nSender		= 1;
	package.nRecipient	= 0;
	package.nDataLength = sizeof(szMessage);
	memcpy(package.lpData, szMessage, sizeof(szMessage)+1);

	cout &lt;&lt; &quot;Sende Daten ... &quot; &lt;&lt; endl;

	//Testpaket senden
	for (int nIndex = 0; nIndex != 1000; nIndex++)
	{
		printf(&quot;\r#%d&quot;, nIndex);
		package.nSender = nIndex;
		Socket.Send(&amp;package);
		Sleep(1);
	}

	delete [] package.lpData;

	cout &lt;&lt; &quot;Alle Daten gesendet !&quot; &lt;&lt; endl;

	return true;
}

bool CreateServer(UINT nPort)
{
	CSocket Socket;

	nPort = 42202;

	//CreateSocket()
	cout &lt;&lt; &quot;Erstelle Socket ... &quot;;

	if (Socket.CreateSocket())
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

	//BindSocket()
	cout &lt;&lt; &quot;Binde Socket an Port #&quot; &lt;&lt; nPort &lt;&lt; &quot; ... &quot;;

	if (Socket.BindSocket(nPort))
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

	//Listen()
	cout &lt;&lt; &quot;Setze Socket in en Listen-Modus ... &quot;;

	if (Socket.Listen())
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

	//Loop
	while (true)
	{
		Socket.Loop();

		//Sleep(1);
	}

	return true;
}
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/topic/230666/socket-verschluckt-daten</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 21:59:01 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/230666.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 30 Dec 2008 15:21:05 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Socket verschluckt Daten on Tue, 30 Dec 2008 15:21:05 GMT]]></title><description><![CDATA[<p>Hallo zusammen,</p>
<p>ich habe angefangen den Quellcode für den Netzwerkteil von dem Buch 3D-Spieleprogrammierung von Stefan Zerbst zu übernehmen. Allerdings habe ich es nicht 1:1 übernommen, sondern etwas abgeändert. Zum einem, verwende ich keine Windows-Events, weil ich den Code ggf später einmal auf Linux übernehmen möchte.<br />
Stattdessen habe ich FD_SETS benutzt um auf Ereignisse reagieren zu können.</p>
<p>Das Programm läuft auf dem eigenen Rechner ganz gut und relativ schnell. Wenn ich aber jetzt den Server auf einen anderen Rechner im Netzwerk starte, dann wird ein großer Teil der Daten &quot;verschluckt&quot;, wenn ich in einer Schleife ca. 1000 Datenpakete sende.</p>
<p>Wenn ich nun ein Sleep(1) in der Schleife einbaue, dann kommen mehr Pakete an, aber es dauert entsprechend lange. Ein kompletter Durchlauf ca 16 Sekunden.</p>
<p>Ich sitze nun 2 Tage an diesem Problem. Weder das Buch noch Google konnten mir bisher helfen. Vielleicht stelle ich auch einfach die falschen Fragen.</p>
<p>Ich poste hier mal den kompletten Code. Schonmal Entschuldigung, falls es zu lang sein sollte, aber ich kenne den Teil nicht, welcher verantwortlich ist für dieses Problem.</p>
<pre><code class="language-cpp">/////////////////////////////////////////////////////////////////////////////
// File      : CSocket.h
// Brief     : Testklasse fuer eine Socketimplementierung
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#ifndef _CSOCKET_H_
#define _CSOCKET_H_

//Includes und libs
#include &lt;winsock2.h&gt;
#include &lt;vector&gt;

#pragma comment (lib, &quot;Ws2_32.lib&quot;)

//Reservierte Nachrichtentypen
enum packagetype_e
{
	TYPE_NEWID,		//Neuer Client bekommt eine ID
	TYPE_NEWCLIENT, //Broadcast an Clients, dass sich ein Client angemeldet hat
	TYPE_REMCLIENT, //Broadcast an Clients, dass sich ein Client abgemeldet hat

	TYPE_CHAR = 50	//Chat Nachricht (Testweise)
};

//Datenpaket
typedef struct netpackage_type
{
	UINT nType;		  //Nachrichtentyp
	UINT nSender;	  //Absender (0 bedeutet Server)
	UINT nRecipient;  //Empfaenger (0 bedeutet Server, 1 bedeutet alle Clients)

	UINT nDataLength; //Datenlaenge von lpData in Bytes
	void *lpData;	  //Zu sendende Daten
} netpackage_t;

//Clients
typedef struct clients_type
{
	UINT	nID;		//ID des Clients
	SOCKET	nSocket;	//Socket vom Client
	char	szIP[256];	//IP-Adresse
	ULONG	nConnected;	//Timestamp der Verbindung
} client_t;

//Socket Klasse
class CSocket
{

private:
	SOCKET  m_nSocket;			//Socket
	UINT    m_nPort;			//Port
	UINT	m_nPackets;			//Anzahl der wartenden Pakete
	UINT	m_nRec;				//Anzahl der Receives
	UINT	m_nMaxSize;			//Maximale Sendegroesse inkl. Header
	FD_SET  m_FdSet;			//Socket Array (http://www.c-worker.ch/tuts/select.php)
	char   *m_cBuffer;//[65536];	//Empfangspuffer
	char   *m_lpSendBuffer;		//Sendepuffer

	//Clients
	std::vector&lt;client_t&gt; m_vClients;

	//Einen Client akzeptieren
	bool Accept();

	//Daten empfangen
	bool Receive(const client_t *lpClient);

	//Schreibe den letzen WSA Error
	void LogLastError(const char *szFunction);

public:
	CSocket();
	~CSocket();

	//Socket Initialisierung
	bool CreateSocket();

	//Socket an einen Port und eine Schnittstelle binden. Wird NULL uebergeben
	//so wird INADDR_ANY benutzt.
	bool BindSocket(UINT nPort, const char *szSocketAddress = NULL);

	//Serverfunktion aktivieren
	bool Listen();

	//Zu einem Server verbinden
	bool Connect(const char *szServer, UINT nServerPort);

	//Daten senden. Diese Funktion wird vom Client und von dem Server benutzt.
	//Der Server muss zusaetzlich den Socket angeben. Clients koennen diesen
	//Parameter ingnorieren.
	bool Send(const char *szData, UINT nSize, SOCKET nReceiver = 0);
	bool Send(const netpackage_t *lpPackage, SOCKET nReceiver = 0);

	bool Disconnect();

	bool IsConnected();

	//Anzahl der verbundenen Clients zureuckgeben
	int GetConnectedClients();

	//TEST
	bool Loop();

};

#endif //_CSOCKET_H_
</code></pre>
<pre><code>/////////////////////////////////////////////////////////////////////////////
// File      : CSocket.cpp
// Brief     : Testklasse fuer eine Socketimplementierung
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#include &quot;CSocket.h&quot;
#include &lt;iostream&gt;

CSocket::CSocket()
{
	m_nSocket  = INVALID_SOCKET;
	m_nPort    = 0;
	m_nPackets = 0;
	m_nRec	   = 0;
	m_nMaxSize = 65536;
	m_cBuffer  = NULL;

	m_lpSendBuffer = new char[m_nMaxSize];
}

CSocket::~CSocket()
{
	//Socket schliessen, falls dieser noch gueltig ist
	if (m_nSocket != INVALID_SOCKET)
		Disconnect();

	//Aufraeumen
	WSACleanup();

	delete [] m_lpSendBuffer;
}

//Einen Client akzeptieren
bool CSocket::Accept()
{
	sockaddr_in saClientAddress;
	int			nClientSize;
	client_t	client;

	//Client akzeptieren
	nClientSize= sizeof(sockaddr_in);
	client.nSocket = accept(m_nSocket, (SOCKADDR*)&amp;saClientAddress, &amp;nClientSize);

	//Erfolgreich ?
	if (client.nSocket == INVALID_SOCKET)
	{
		LogLastError(&quot;Accept&quot;);
		return false;
	}

	//Informationen abfragen
	SOCKADDR_IN sai; 

    int sai_len = sizeof( SOCKADDR_IN ); 
        memset( &amp;sai, 0, sizeof( SOCKADDR_IN ) ); 
        getpeername( client.nSocket, (SOCKADDR*)&amp;sai, &amp;sai_len ); 

    strcpy(client.szIP , inet_ntoa( sai.sin_addr ) ); 

	printf(&quot;New Client : %s\n&quot;, client.szIP);

	//Client hinzufuegen
	m_vClients.push_back(client);

	return true;
}

//Daten empfangen
bool CSocket::Receive(const client_t *lpClient)
{
	UINT nSize	= 65536;	//Maximale Bytes pro Durchlauf
	UINT nRead	= 0;		//Anzahl gelesene Bytes
	UINT nSeek	= 0;		//Position im Buffer
	UINT nLeft	= 0;		//Verbleibende Bytes
	bool bDone	= false;	//True, wenn fertig
	bool bFirst = true;		//True beim dem ersten Schleifendurchlauf

	m_cBuffer = new char[nSize];

	//Zeiger auf Netzwerkpaket
	netpackage_t *lpPackage = NULL;

	//Totale groesse des Paketes
	UINT nPackageSize		= 0;

	//Groesse eines Header
	UINT nDefPackageSize	= sizeof(netpackage_t);

	//Debug
	m_nRec++;
	printf(&quot;Empfange Daten (%d) von %s\n&quot;, m_nRec, lpClient-&gt;szIP);

	//Bei jedem Schleifendurchlauf nSize Bytes Daten lesen, bis
	//keine mehr vorhanden sind
	while (!bDone)
	{
		nRead = recv(lpClient-&gt;nSocket, &amp;m_cBuffer[nLeft], nSize - nLeft, 0);

		//Client weg ?
		if (bFirst)
		{
			if (nRead == 0)
				return false;

			bFirst = false;
		}

		//Fehler ?
		if (nRead == SOCKET_ERROR)
		{
			int nError = WSAGetLastError();

			//Kritischer Fehler ?
			if ((nError != WSAEMSGSIZE) &amp;&amp; (nError != WSAEWOULDBLOCK))
			{
				bDone = true;

				printf (&quot;\a\a\aAchtung Komischer Fehler !\n&quot;);
				return false;
			}
		}

		//Nun sind nRead Bytes im Buffer
		if (nRead &lt;= 0)
			bDone = true;
		else
		{
			//Alte Daten im Buffer beachten
			nRead += nLeft;

			//Loopen, bis wir einen kompletten Header finden
			while ((nRead - nSeek) &gt;= nDefPackageSize) // Ergaenzt : '='
			{
				//Naechstes Datenstueck
				lpPackage    = (netpackage_t*)&amp;m_cBuffer[nSeek];
				lpPackage-&gt;lpData = &amp;m_cBuffer[nSeek] + nDefPackageSize;
				nPackageSize = nDefPackageSize + lpPackage-&gt;nDataLength;

				//Haben wir das ganze Datenpaket empfangen ?
				if ((nRead - nSeek) &gt;= nPackageSize)
				{

					//TODO : Enqueue Message
					//printf(&quot;\r#%d    &quot;, m_nPackets);

					if (lpPackage-&gt;nType == TYPE_CHAR)
					{

						m_nPackets++;

						printf(&quot;ID : %d - # : %d) : %s\n&quot;, lpPackage-&gt;nSender, m_nPackets, lpPackage-&gt;lpData);
						std::cout &lt;&lt; m_cBuffer &lt;&lt; std::endl;
					}
					else
					{
						//m_nPackets--;

						printf(&quot;Paket #%d Fehlerhaft.\n&quot;, m_nPackets);
					}

					nSeek += nPackageSize;
				}
				else
				{
					//Das Bruchstueck an den Anfang des Buffers kopieren und weiterlesen
					memcpy(m_cBuffer, &amp;m_cBuffer[nSeek], nRead - nSeek);
					nLeft = nRead - nSeek;

					break;
				}
			}

			//Haben wir alle Daten ?
			if (nRead &lt;= nSize) //Ergaenzt : '='
				bDone = true;

		}
	}//while

	//printf(&quot;[%d] %s\n&quot;, m_nPackets, m_cBuffer);

	Sleep(1);

	delete [] m_cBuffer;

	return true;
}

//Schreibe den letzten WSA Error
void CSocket::LogLastError(const char *szFunction)
{
	UINT nLastError = WSAGetLastError();

	if (nLastError == 0)
		return;

	std::cout &lt;&lt; &quot;Socket Error : [&quot; &lt;&lt; szFunction &lt;&lt; &quot;] &quot;;

	switch (nLastError)
	{
		case WSANOTINITIALISED:
			std::cout &lt;&lt; &quot;Successful WSAStartup not yet performed.&quot; &lt;&lt; std::endl;
			break;

		case WSAEADDRNOTAVAIL:
			std::cout &lt;&lt; &quot;Cannot assign requested address. The requested address is not valid on this local computer.&quot; &lt;&lt; std::endl;
			break;

		case WSAHOST_NOT_FOUND:
			std::cout &lt;&lt; &quot;Host not found.&quot; &lt;&lt; std::endl;
			break;

		case WSAECONNREFUSED:
			std::cout &lt;&lt; &quot;Connection refused.&quot; &lt;&lt; std::endl;
			break;

		default:
			std::cout &lt;&lt; &quot;Unknown WSA Error #&quot; &lt;&lt; nLastError &lt;&lt; std::endl;
			break;
	}
}

//Socket erzeugen
bool CSocket::CreateSocket()
{
	WORD wVersionRequested;
    WSADATA wsaData;
	int nError;	

	//WSA Starten
	wVersionRequested = MAKEWORD(2, 2);
    nError = WSAStartup(wVersionRequested, &amp;wsaData);

	if (nError != 0)
	{
		LogLastError(&quot;CreateSocket() WSAStartup&quot;);
		return false;
	}

	//Existiert bereits ein Socket ?
	if (m_nSocket != INVALID_SOCKET)
		if (Disconnect() == false)
			return false;

	//Socket erstellen
	m_nSocket = socket(AF_INET, SOCK_STREAM, 0);

	//Erfolgreich ?
	if (m_nSocket == INVALID_SOCKET)
	{
		LogLastError(&quot;CreateSocket() socket(AF_INET, SOCK_STREAM, 0)&quot;);
		return false;
	}

	return true;
}

//Socket an einen Port und eine Schnittstelle binden. Wird NULL uebergeben
//so wird INADDR_ANY benutzt.
bool CSocket::BindSocket(UINT nPort, const char *szSocketAddress)
{
	sockaddr_in saServerAddress;

	memset(&amp;saServerAddress, 0, sizeof(sockaddr_in));

	saServerAddress.sin_family		= AF_INET;

	if (szSocketAddress != NULL)
		saServerAddress.sin_addr.s_addr = inet_addr(szSocketAddress);
	else
		saServerAddress.sin_addr.s_addr = INADDR_ANY;//inet_addr(&quot;127.0.0.1&quot;);

	saServerAddress.sin_port		= htons(nPort);

	//Socket binden
	if (bind(m_nSocket, (SOCKADDR*)&amp;saServerAddress, sizeof(saServerAddress)) == SOCKET_ERROR)
	{
		LogLastError(&quot;BindSocket()&quot;);
		Disconnect();

		return false;
	}

	return true;
}

//Socket in den Listen-Modus setzen
bool CSocket::Listen()
{
	if (listen(m_nSocket, 64) != 0)
	{
		LogLastError(&quot;Listen()&quot;);
		return false;
	}

	return true;
}

//Zu einem Server verbinden
bool CSocket::Connect(const char *szServer, UINT nServerPort)
{
	sockaddr_in saServerAddress;
	LPHOSTENT	lpHost = NULL;

	//Es wird versucht den Server zu finden
	memset(&amp;saServerAddress, 0, sizeof(sockaddr_in));

	saServerAddress.sin_port = htons(nServerPort);
	saServerAddress.sin_family = AF_INET;
	saServerAddress.sin_addr.S_un.S_addr = inet_addr(szServer);

	//Falls ein Name und keine IP uebergeben wurde, wird versucht
	//diesen aufzuloesen.
	if (saServerAddress.sin_addr.S_un.S_addr == INADDR_NONE)
	{
		lpHost = gethostbyname(szServer);

		if (lpHost != NULL)
			saServerAddress.sin_addr.S_un.S_addr = ((LPIN_ADDR)lpHost-&gt;h_addr)-&gt;S_un.S_addr;
		else
		{
			LogLastError(&quot;gethostbyname()&quot;);
			return false;
		}

	}

	//Zum Server verbinden
	if (connect(m_nSocket, (SOCKADDR*)&amp;saServerAddress, sizeof(sockaddr)) == SOCKET_ERROR)
	{
		LogLastError(&quot;connect()&quot;);

		Disconnect();
		return false;
	}

	//TODO : Erfolgreichen Verbindungsstatus setzen

	return true;
}

//Daten senden. Diese Funktion wird vom Client und von dem Server benutzt.
//Der Server muss zusaetzlich den Socket angeben. Clients koennen diesen
//Parameter ingnorieren.
bool CSocket::Send(const char *szData, UINT nSize, SOCKET nReceiver)
{
	UINT nSend = 0;
	UINT nLeft = 0;

	if (nReceiver == 0)
		nReceiver = m_nSocket;

	while (nSend &lt; nSize)
	{
		nLeft = send(nReceiver, szData + nSend, nSize - nSend, 0);

		//Erfolgreich ?
		if (nLeft == SOCKET_ERROR)
		{
			LogLastError(&quot;send()&quot;);

			return false;
		}

		if (nLeft == nSize)
			break;

		printf(&quot;nLeft = %d\n&quot;, nLeft);

		nSend += nLeft;
	}

	return true;
}

bool CSocket::Send(const netpackage_t *lpPackage, SOCKET nReceiver)
{
	UINT nSizeTotal = sizeof(netpackage_t) + lpPackage-&gt;nDataLength;
	//char *lpBuffer = new char[m_nMaxSize]; //TODO : Eventuell als Member ?

	//Daten-Serialisierung
	memcpy(m_lpSendBuffer, lpPackage, sizeof(netpackage_t));
	memcpy(m_lpSendBuffer + sizeof(netpackage_t), lpPackage-&gt;lpData, lpPackage-&gt;nDataLength);

	//Daten Senden
	Send(m_lpSendBuffer, nSizeTotal, nReceiver);

	return true;
}

//Socket herunterfahren und schliessen
bool CSocket::Disconnect()
{
	if (m_nSocket == INVALID_SOCKET)
		return true;

	//Socket runterfahren
	shutdown(m_nSocket, SD_BOTH);

	//Socket schliessen
	closesocket(m_nSocket);

	m_nSocket = INVALID_SOCKET;

	return true;
}

//Anzahl der verbundenen Clients zureuckgeben
int CSocket::GetConnectedClients()
{
	return m_vClients.size();
}

//TEST
bool CSocket::Loop()
{
	int nSocketState;

	// Inhalt vom FD_SET leeren
	FD_ZERO(&amp;m_FdSet); 

	//Den Accept-Socket hinzufuegen
	FD_SET(m_nSocket, &amp;m_FdSet); 

	//Hier werden alle gueltigen Client Sockets dem FD_SET hinzugefuegt werden
	for (std::vector&lt;client_t&gt;::iterator it = m_vClients.begin(); it != m_vClients.end(); ++it)
	{
		//std::cout &lt;&lt; &quot;Client im Vector : &quot; &lt;&lt; it-&gt;szIP &lt;&lt; std::endl;

		if (it-&gt;nSocket != INVALID_SOCKET)
			FD_SET(it-&gt;nSocket, &amp;m_FdSet);
	}

	//Nun wird geschaut, welche Sockets Daten bereitstellen (oder accept() beim Server Socket)
	//Alle Clients, welche keine Daten haben, werden aus diesem Array entfernt.
	nSocketState = select(0, &amp;m_FdSet, NULL, NULL, NULL);

	//Erfolgreich ?
	if (nSocketState == INVALID_SOCKET)
	{
		LogLastError(&quot;select()&quot;);
		return false;
	}

	//Wird auf eine neue Verbindung gewartet ?
	if(FD_ISSET(m_nSocket, &amp;m_FdSet)) 
		Accept();

	//Die Clients werden durchlaufen und mit den empfangsbereiten Sockets verglichen.
	//Bei einer uebereinstimmung werden die Daten empfangen
	for (std::vector&lt;client_t&gt;::iterator it = m_vClients.begin(); it != m_vClients.end(); ++it)
		if (FD_ISSET(it-&gt;nSocket, &amp;m_FdSet))
		{
			if (!Receive(&amp;*(it)))
			{
				printf(&quot;Entferne Client.\n&quot;); //TODO : Wenn false, client entfernen und socket schliessen

				//Test
				m_vClients.erase(it);
				break;
			}
		}

	return false;
}
</code></pre>
<pre><code class="language-cpp">/////////////////////////////////////////////////////////////////////////////
// File      : main.h
// Brief     :
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#ifndef _MAIN_H_
#define _MAIN_H_

#include &quot;CSocket.h&quot;

//Prototypen
bool ConnectToServer(const char *szServer, UINT nPort);
bool CreateServer(UINT nPort);

#endif //_MAIN_H_
</code></pre>
<pre><code class="language-cpp">/////////////////////////////////////////////////////////////////////////////
// File      : main.cpp
// Brief     :
// Created	 : 29.12.2008
/////////////////////////////////////////////////////////////////////////////

#include &quot;main.h&quot;
#include &lt;iostream&gt;

using namespace std;

int main(int argc, char *argv)
{
	char cChoice = 0;

	cout &lt;&lt; &quot;Socket Testprogramm&quot; &lt;&lt; endl
		 &lt;&lt; &quot;================================================&quot; &lt;&lt; endl &lt;&lt; endl
		 &lt;&lt; &quot;Moechten Sie einen Server (s) oder einen Client (c) starten ? (q) fuer Quit :&quot; &lt;&lt; endl &lt;&lt; endl;

	do
	{
		cout &lt;&lt; &quot;Auswahl : &quot;;
		cin &gt;&gt; cChoice;
		cin.clear();
		cin.sync();

		if (cChoice == 's')
		{
			CreateServer(42202);

			return 0;
		}

		if (cChoice == 'c')
		{
			ConnectToServer(&quot;127.0.0.1&quot;, 42202);

			return 0;
		}

	} while(cChoice != 'q');

	return 0;
}

bool ConnectToServer(const char *szServer, UINT nPort)
{
	CSocket Socket;

	//CreateSocket()
	cout &lt;&lt; &quot;Erstelle Socket ... &quot;;

	if (Socket.CreateSocket())
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
	{
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

		return false;
	}

	//Verbinde zum Server
	//CreateSocket()
	cout &lt;&lt; &quot;Verbinde zum Server [&quot; &lt;&lt; szServer &lt;&lt; &quot;:&quot; &lt;&lt; nPort &lt;&lt; &quot;] ... &quot;;

	if (Socket.Connect(szServer, nPort))
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
	{
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

		return false;
	}

	//Testpaket
	netpackage_t package;

	char szMessage[256] = &quot;Hallo Welt, wie geht es Dir denn heute so :)&quot;;
	package.lpData = new char[1024];

	package.nType		= TYPE_CHAR;
	package.nSender		= 1;
	package.nRecipient	= 0;
	package.nDataLength = sizeof(szMessage);
	memcpy(package.lpData, szMessage, sizeof(szMessage)+1);

	cout &lt;&lt; &quot;Sende Daten ... &quot; &lt;&lt; endl;

	//Testpaket senden
	for (int nIndex = 0; nIndex != 1000; nIndex++)
	{
		printf(&quot;\r#%d&quot;, nIndex);
		package.nSender = nIndex;
		Socket.Send(&amp;package);
		Sleep(1);
	}

	delete [] package.lpData;

	cout &lt;&lt; &quot;Alle Daten gesendet !&quot; &lt;&lt; endl;

	return true;
}

bool CreateServer(UINT nPort)
{
	CSocket Socket;

	nPort = 42202;

	//CreateSocket()
	cout &lt;&lt; &quot;Erstelle Socket ... &quot;;

	if (Socket.CreateSocket())
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

	//BindSocket()
	cout &lt;&lt; &quot;Binde Socket an Port #&quot; &lt;&lt; nPort &lt;&lt; &quot; ... &quot;;

	if (Socket.BindSocket(nPort))
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

	//Listen()
	cout &lt;&lt; &quot;Setze Socket in en Listen-Modus ... &quot;;

	if (Socket.Listen())
		cout &lt;&lt; &quot;Erfolgreich !&quot; &lt;&lt; endl;
	else
		cout &lt;&lt; &quot;Fehlgeschlagen !&quot; &lt;&lt; endl;

	//Loop
	while (true)
	{
		Socket.Loop();

		//Sleep(1);
	}

	return true;
}
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/1637321</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1637321</guid><dc:creator><![CDATA[Socket__]]></dc:creator><pubDate>Tue, 30 Dec 2008 15:21:05 GMT</pubDate></item></channel></rss>