Skip to main content
gamedev.net
🔒 Locked 🎮 Unity

Multithreading Problem

Started by dev578 Aug 20, 2006 at 9:06 PM 12 replies 2.2k views
Original Post
dev578
dev578
I don't know if this is a good system or not, but on the computer that is going to host my game, three threads are spawned. One is to listen for new connections (it also eventually needs to check for dead connections and keep the player connectivity info up to date). Another thread recieves information from a client and sends it out to the other clients. The last thread is the core thread that processes enemy information and AI and whatnot. I am able to produce a somewhat minimal example of my problem. Using the three threads as-is shown below, my game does some funny things. It starts out at about 98 fps, then after maybe 10-15 seconds it drops to around 47 frames per second and laggs something nasty. If you leave it like that for awile it eventually freezes. Not using the three threads, It stays at about 130 frames per second and doesn't drop or freeze. Here are the three threads, all are spawned in the order they are in the code when the host computer clicks on host.

DWORD WINAPI ListeningThread(LPVOID pParam)
{
	CMultiPlayer *p = (CMultiPlayer*)pParam;
	
	// Wait for a client

	sockaddr_in addr = {0};
	int Size = sizeof(addr);

	p->ClientNumber = -1;

	fd_set Input_set, Exc_set;
	int s;
	timeval timeout;

	timeout.tv_sec = 4;
	timeout.tv_usec = 4000000;

	FD_ZERO(&Input_set);
	FD_ZERO(&Exc_set);
	FD_SET(p->listeningSocket, &Input_set);
	FD_SET(p->listeningSocket, &Exc_set);

	while (true)
	{
		// Do the select
		s = select(0, &Input_set, NULL, &Exc_set, &timeout);

		if (p->CloseThreads == true)
		{
			p->ThreadCount--;
			//OutputDebugString("ListeningThread returned\n");
			return 0;
		}
		
		if (s > 0 && !FD_ISSET(p->listeningSocket, &Exc_set) && FD_ISSET(p->listeningSocket, &Input_set))
		{
			p->ClientNumber++;

			p->theClient[p->ClientNumber] = accept(p->listeningSocket,
					(sockaddr*)&addr,
					&Size);

			if (p->theClient[p->ClientNumber] == INVALID_SOCKET)
			{
				//nret = WSAGetLastError();
				//ReportError(nret, "accept()");

				OutputDebugString("accept failed\n");

				WSACleanup();
				return 1;
			}

			sprintf(p->ActionText[p->CurrentArray],"Someone Connected");
			if (p->CurrentArray != 3)
				p->CurrentArray++;

			p->nClients++;
			p->nClientArray[p->ClientNumber] = 1;

			//send the client a packet with his client ID and the number of players and enemies
			char buf[4];
			buf[0] = 2;
			buf[1] = (char)p->ClientNumber;
			buf[2] = (char)p->nNumEnemies;
			buf[3] = (char)p->nNumPlayers;
		
			int Size = 4;
			p->sendall(p->theClient[p->ClientNumber],buf,&Size);
		}
	}
	return 0;
}

DWORD WINAPI ClientCommunicationsThread(LPVOID pParam)
{
	CMultiPlayer *p = (CMultiPlayer*)pParam;
	int nret;
	char locationbuffer[256];

	fd_set Input_set, Exc_set;
	int s;
	timeval timeout;

	timeout.tv_sec = 4;
	timeout.tv_usec = 4000000;

	FD_ZERO(&Input_set);
	FD_ZERO(&Exc_set);
	for (int i = 0; i < p->nClients; i++)
	{
		FD_SET(p->theClient, &Input_set);
		FD_SET(p->theClient, &Exc_set);
	}

	while (true)
	{
		s = select(0, &Input_set, NULL, &Exc_set, &timeout);

		if (p->CloseThreads == true)
		{
			p->ThreadCount--;
			//OutputDebugString("ClientThread returned\n");
			return 0;
		}

		if (s > 0)
		{
			for (int i = 0; i < p->nClients; i++)
			{
				if (!FD_ISSET(p->theClient, &Exc_set) && FD_ISSET(p->theClient, &Input_set))
				{
					nret = recv(p->theClient,locationbuffer,sizeof(locationbuffer),0);
					if (nret != -1)
					{
						for (int c = 0; c < p->nClients; c++)
						{
							if (p->nClientArray[c] == 1 && c != i)
							{
								p->sendall(p->theClient[c],locationbuffer,&nret);
							}
						}
					}
				}
			}
		}
	}
	return 0;
}

DWORD WINAPI ServerThread(LPVOID pParam)
{
	CMultiPlayer *p = (CMultiPlayer*)pParam;
	char buf[3];
	int OldAction = 0;

	while (true)
	{
		if (p->CloseThreads == true)
		{
			p->ThreadCount--;
			//OutputDebugString("ServerThread returned\n");
			return 0;
		}
		for (int i = 0; i < p->nNumEnemies; i++)
		{
			p->m_NEnemy.Update();
			OldAction = p->m_NEnemy.Action;
			p->m_NEnemy.AIMove();
			//look for a change of action
			if (OldAction != p->m_NEnemy.Action)
			{
				OldAction = p->m_NEnemy.Action;
				//ID for an enemy packet
				buf[0] = 3;
				//ID of the enemy
				buf[1] = (char)i;
				//New action of the enemy
				buf[2] = (char)(p->m_NEnemy[0].Action);
				for (int i = 0; i < p->nNumPlayers; i++)
				{
					int Size = 3;
					int retval = p->sendall(p->theClient[0],buf,&Size);
				}
				//TODO snap enemy position with NEnemy position
			}
		}
		Sleep(100);
	}
	return 0;
}



Any ideas? Thank you Dev578 [Edited by - dev578 on August 28, 2006 9:49:03 AM]
bit64
bit64
I don't see any render code in here, when you say frames per second, you are talking about rendering frames correct? I will assume so. One of the problems with your code is that your server is sending massive numbers of messages to the clients without seeing if your clients have processed them. Hopefully your client is rendering on a different thread than it is receiving messages on, but if not then the client cannot keep up with this kind of message traffic. You should only send out a few packets of information to the client, until the client has confirmed that they have been processed.




HTH

Also, the reason this would work with only one thread, is that the server would have more work to do so the message velocity to the client would decrease.

Ordered_disorder also makes a very valid point below....
Don't be afraid to be yourself. Nobody else ever will be.
ordered_disorder
ordered_disorder
I noticed you didn't have any wait or sleep functions in the first two threads, maybe i missed them, but that is probably eating up all your cpu power. Having a lone thread basically doing while(1) { } devastates cycles.
dev578
dev578
Thank you for your replies.

No there is no rendering, these threads are on the server, it stores the information. The clients do the rendering.

As for the first thread, it just recieves connections. select() blocks, so sleep isn't neccessary unless a rapid amount of people are connecting. The second thread does have the potential of flooding with too much data. I am going to have to have the client tell the server it got the message as you say.

However, in this test not much information is being sent. Packets are only sent when the players action changes.

I did add sleep()'s to the first two and something interesting happened. No longer does the wierd drop in framerate happen, and the framerate is at a constant 130 (with the threads operational) right up until the FREEZE. I can't get the freeze to happen unless the threads are running.

I don't see a problem, unless this is a nasty multithreaded-specific issue.

Dev578
paulecoyote
paulecoyote
Not all of your threads have a Sleep in them

You need Sleep(1) or something in each of the while(true) threads to allow the others to get a slice of time.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Bregma
Bregma
Sorry, it's unclear what the problem is. Can you check if one of the threads on the server has gotten bunged up (meaning the problem is your code) or if the server is running fine and the clients are just not receiving broadcasts (problem is in the comms)?
Stephen M. Webb
Professional Free Software Developer
dev578
dev578
The reason why this is so hard for me to figure out is because it is working perfectly the way I want it to right up until the program freezes. The communication between client and server are working great. I'm not so sure anymore that the problem is with the threads, but it is definately with my networking class. Not using the networking class at all, the game doesn't freeze. I have also been able to prove that my enemy, player, and model classes aren't freezing, they work fine when not networking. So I really hate to do this, but here is my code exactly as it is now, it has frozen anywhere from two minutes in to a half hour in:

//header file
#include <winsock.h>#include <windows.h>#include <stdio.h>#pragma comment(lib,"wsock32.lib")//waits for connectionsDWORD WINAPI ListeningThread(LPVOID pParam);//one for each clientDWORD WINAPI ClientCommunicationsThread(LPVOID pParam);//takes care of enemies and other serverwide stats, sends to clientsDWORD WINAPI ServerThread(LPVOID pParam);//TODO: test what happens if server gets backed up#define ID_LEFT 0#define ID_RIGHT 1#define ID_DOWN 2#define ID_UP 3#include "CPlayer.h"#include "CNPlayer.h"#include "CEnemy.h"#include "CNEnemy.h"class CMultiPlayer{public:	//constructor	CMultiPlayer();	//destructor	~CMultiPlayer();	//bool to tell threads to shut down	bool CloseThreads;	//amount of open threads	int ThreadCount;	//set to true on the hosts computer, false on clients	bool bHosting;	//socket used to accept connections	SOCKET listeningSocket;	//a socket for each client	SOCKET theClient[10];	//used by the server to give consecutive client id's out, replace by using the nClientArray	int ClientNumber;	//socket that client uses to send and recieve to server 	SOCKET theSocket;	//called by the host computer, additionally loads network enemy and player info, spawns three threads	int Host(int Port);	//connect to server	int Connect(char *IP, int Port);	//used to send text to server	int SendText(char *Text);	//used to send player location to server	int SendLocation(int ChangeID, int Value);	//used to get information from server	int GetInfoFromServer();	//make sure all information is sent	int sendall(SOCKET s, char *buf, int *len);	//local copy of the d3ddevice	IDirect3DDevice9* m_pD3DDevice;	//function allowing user to give the device	int GiveD3DDevice(IDirect3DDevice9 *pD3DDevice);	//used to render the other players	int RenderNPlayers();	//used to render enemies	int RenderEnemies();		//for everyone	int LoadPlayers();	int LoadEnemies();		//for the host	int LoadNPlayers();	int LoadNEnemies();	//clean everything up, shuts down threads if hosting	int Cleanup();	//used as an ID for threads	DWORD ThreadID;	//number of enemies and players, sent by server	int nNumEnemies, nNumPlayers;	//class pointers for players and enemies, N's only used by server	CPlayer *m_Player;	CNPlayer *m_NPlayer;	CEnemy *m_Enemy;	CNEnemy *m_NEnemy;	//text that main function polls and prints to the edit box	char ActionText[5][256];	//tracker for the action text	int CurrentArray;		//number of clients, variable used by server	int nClients;	//array position = client id, value of one in use, zero not in use	int nClientArray[10];	//one array pos for each client, increased by server everytime it sends something	//client sends a packet back letting the server know it recieved the information	//then the server decreases the value	//used to prevent backups	int NeedRecvNotificationPacket[10];	//the clients ID assigned by the server	int ClientID;};

//cpp file
#include "CMultiPlayer.h"CMultiPlayer::CMultiPlayer(){	bHosting = false;	for (int i = 0; i < 5; i ++)	{		for (int i2 = 0; i2 < 256; i2++)		{			ActionText[i2] = 0;				}	}	CloseThreads = false;	ThreadCount = 0;	CurrentArray = 0;	nNumEnemies = 0; nNumPlayers = 0;	m_Player = NULL;	m_NPlayer = NULL;	m_Enemy = NULL;	m_NEnemy = NULL;	for (int i = 0; i < 10; i++)		NeedRecvNotificationPacket = 0;}CMultiPlayer::~CMultiPlayer(){	Cleanup();}int CMultiPlayer::Host(int Port){	//hardcoded values, we know exactly how much at this point in time	nNumEnemies = 2;	nNumPlayers = 2;	WORD sockVersion;	WSADATA wsaData;	int nret;	//use winsock version 1.1	sockVersion = MAKEWORD(1, 1);	//begin by initializing Winsock	WSAStartup(sockVersion, &wsaData);	//create the listening socket	listeningSocket = socket(AF_INET,	// Go over TCP/IP		SOCK_STREAM,					// This is a stream-oriented socket		IPPROTO_TCP);					// Use TCP rather than UDP	if (listeningSocket == INVALID_SOCKET)	{		//nret = WSAGetLastError();		// Get a more detailed error		//ReportError(nret, "socket()");		// Report the error with our custom function		OutputDebugString("listening socket is invalid\n");		WSACleanup();				// Shutdown Winsock		return 1;			// Return an error value	}	// Use a SOCKADDR_IN struct to fill in address information	SOCKADDR_IN serverInfo;	serverInfo.sin_family = AF_INET;	serverInfo.sin_addr.s_addr = INADDR_ANY;	// Since this socket is listening for connections,							// any local address will do	serverInfo.sin_port = htons(Port);		// Convert integer 8888 to network-byte order							// and insert into the port field	// Bind the socket to our local server address	nret = bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));	if (nret == SOCKET_ERROR) {		//nret = WSAGetLastError();		//ReportError(nret, "bind()");		OutputDebugString("bind failed\n");		WSACleanup();		return 1;	}	// Make the socket listen	nret = listen(listeningSocket, 10);		// Up to 10 connections may wait at any							// one time to be accept()'ed	if (nret == SOCKET_ERROR) {		//nret = WSAGetLastError();		//ReportError(nret, "listen()");		OutputDebugString("listen failed\n");		WSACleanup();		return 1;	}	char ac[80];    	if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)	{        OutputDebugString("could not get host name\n");        return 1;    }    	struct hostent *phe = gethostbyname(ac);    	if (phe == 0)	{        OutputDebugString("gethostbyname failed\n");        return 1;    }	sprintf(ActionText[CurrentArray],"Hosting game with IP's:\n");	if (CurrentArray != 3)		CurrentArray++;    for (int i = 0; phe->h_addr_list != 0; i++)	{        struct in_addr addr;        memcpy(&addr, phe->h_addr_list, sizeof(struct in_addr));        sprintf(ActionText[CurrentArray],"%s\n",inet_ntoa(addr));		if (CurrentArray != 3)			CurrentArray++;    }	LoadNPlayers();	LoadNEnemies();	if (CreateThread(NULL,0,&ListeningThread,this,0,&ThreadID) != NULL)	{		ThreadCount++;	}	else	{		OutputDebugString("Unable to create ListeningThread\n");		return 1;	}	if (CreateThread(NULL,0,&ClientCommunicationsThread,this,0,&ThreadID) != NULL)	{		ThreadCount++;	}	else	{		OutputDebugString("Unable to create ClientCommunicationsThread\n");		return 1;	}	if (CreateThread(NULL,0,&ServerThread,this,0,&ThreadID) != NULL)	{		ThreadCount++;	}	else	{		OutputDebugString("Unable to create ServerThread\n");		return 1;	}	bHosting = true;	return 0;}int CMultiPlayer::Connect(char* IP, int Port){	WORD sockVersion;	WSADATA wsaData;	int nret;	sockVersion = MAKEWORD(1, 1);	// Initialize Winsock as before	WSAStartup(sockVersion, &wsaData);	// Store information about the server	LPHOSTENT hostEntry;	in_addr iaHost;	if (bHosting == false)		iaHost.s_addr = inet_addr(IP);	else		iaHost.s_addr = inet_addr("127.0.0.1");	hostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET);	if (!hostEntry) {		//nret = WSAGetLastError();		//ReportError(nret, "gethostbyname()");	// Report the error as before		OutputDebugString("gethostbyaddr failed\n");		WSACleanup();		return 1;	}	theSocket = socket(AF_INET,			// Go over TCP/IP			   SOCK_STREAM,			// This is a stream-oriented socket			   IPPROTO_TCP);		// Use TCP rather than UDP	if (theSocket == INVALID_SOCKET) {		//nret = WSAGetLastError();		//ReportError(nret, "socket()");		OutputDebugString("socket failed\n");		WSACleanup();		return 1;	}	// Fill a SOCKADDR_IN struct with address information	SOCKADDR_IN serverInfo;	serverInfo.sin_family = AF_INET;	// At this point, we've successfully retrieved vital information about the server,	// including its hostname, aliases, and IP addresses.  Wait; how could a single	// computer have multiple addresses, and exactly what is the following line doing?	// See the explanation below.	serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);	serverInfo.sin_port = htons(Port);		// Change to network-byte order and							// insert into port field	// Connect to the server	nret = connect(theSocket,		       (LPSOCKADDR)&serverInfo,		       sizeof(struct sockaddr));	if (nret == SOCKET_ERROR) {		//nret = WSAGetLastError();		//ReportError(nret, "connect()");		char ErrorString[512];		sprintf(ErrorString,"connect failed in CMultiPlayer::Connect(), returned error %i\n",WSAGetLastError());		OutputDebugString(ErrorString);		WSACleanup();		return 1;	}	u_long iMode = 1;	ioctlsocket(theSocket, FIONBIO, &iMode);	return 0;}DWORD WINAPI ListeningThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;		// Wait for a client	sockaddr_in addr = {0};	int Size = sizeof(addr);	p->ClientNumber = -1;	fd_set Input_set, Exc_set;	int s;	timeval timeout;	timeout.tv_sec = 4;	timeout.tv_usec = 4000000;	FD_ZERO(&Input_set);	FD_ZERO(&Exc_set);	FD_SET(p->listeningSocket, &Input_set);	FD_SET(p->listeningSocket, &Exc_set);	while (true)	{		// Do the select		s = select(0, &Input_set, NULL, &Exc_set, &timeout);		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ListeningThread returned\n");			return 0;		}				if (s > 0 && !FD_ISSET(p->listeningSocket, &Exc_set) && FD_ISSET(p->listeningSocket, &Input_set))		{			p->ClientNumber++;			p->theClient[p->ClientNumber] = accept(p->listeningSocket,					(sockaddr*)&addr,					&Size);			if (p->theClient[p->ClientNumber] == INVALID_SOCKET)			{				//nret = WSAGetLastError();				//ReportError(nret, "accept()");				OutputDebugString("accept failed\n");				WSACleanup();				return 1;			}			sprintf(p->ActionText[p->CurrentArray],"Someone Connected");			if (p->CurrentArray != 3)				p->CurrentArray++;			p->nClients++;			p->nClientArray[p->ClientNumber] = 1;			//send the client a packet with his client ID and the number of players and enemies			char buf[4];			buf[0] = 2;			buf[1] = (char)p->ClientNumber;			buf[2] = (char)p->nNumEnemies;			buf[3] = (char)p->nNumPlayers;					int Size = 4;			p->sendall(p->theClient[p->ClientNumber],buf,&Size);			p->NeedRecvNotificationPacket[p->ClientNumber]++;		}		Sleep(100);	}	return 0;}DWORD WINAPI ClientCommunicationsThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;	int retval = 0;	char incommingbuffer[512];	int Tracker = 0;	int PacketSize = 0;	fd_set Input_set, Exc_set;	int s;	timeval timeout;	timeout.tv_sec = 4;	timeout.tv_usec = 4000000;	FD_ZERO(&Input_set);	FD_ZERO(&Exc_set);	for (int i = 0; i < p->nClients; i++)	{		FD_SET(p->theClient, &Input_set);		FD_SET(p->theClient, &Exc_set);	}	while (true)	{		s = select(0, &Input_set, NULL, &Exc_set, &timeout);		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ClientThread returned\n");			return 0;		}		if (s > 0)		{			for (int i = 0; i < p->nClients; i++)			{				if (!FD_ISSET(p->theClient, &Exc_set) && FD_ISSET(p->theClient, &Input_set))				{					char buffer[256];					for (int j = 0; j < 256; i++)						buffer[j] = 0;					retval = recv(p->theClient,incommingbuffer,sizeof(incommingbuffer),0);					if (retval == -1 || retval == 0) return 0;					//slap the buffer we recieved onto the end of incommingbuffer					for (int j = 0; j < retval; j++)					{						incommingbuffer[Tracker] = buffer[j];						Tracker++;					}					bool bGoodToGo = false;					while (bGoodToGo == false)					{						if (incommingbuffer[0] == 0)							PacketSize = 256;						else if (incommingbuffer[0] == 1)							PacketSize = 31;						else if (incommingbuffer[0] == 2)							PacketSize = 4;						else if (incommingbuffer[0] == 3)							PacketSize = 3;						//char TempString[512];						//sprintf(TempString,"Next type in line to be processed: %i  retval: %i\n",PacketSize,retval);						//OutputDebugString(TempString);						//packet done						if (Tracker >= PacketSize)						{							//OutputDebugString("Complete Packet\n");							//transfer to buffer, snug up incommingbuffer, decrease tracker by packetsize, break							for (int j = 0; j < PacketSize; j++)								buffer[j] = incommingbuffer[j];							int TrackerTwo = PacketSize;							if (PacketSize != Tracker)							{								//OutputDebugString("Packet overwrote, need to snug\n");								for (int j = 0; j < 512; j++)								{									incommingbuffer = incommingbuffer[TrackerTwo];									TrackerTwo++;									if (TrackerTwo >= Tracker)										break;								}								for (int j = TrackerTwo; j < 512; j++)								{									incommingbuffer[j] = 0;								}							}							Tracker -= PacketSize;							if (Tracker != 0)								bGoodToGo = false;							else								bGoodToGo = true;							//char TestString[512];							//sprintf(TestString,"Tracker: %i\n",Tracker);							//OutputDebugString(TestString);						}						else						{							//OutputDebugString("Incomplete Packet\n");							//bGoodToGo = true;							//return 0;							break;						}						if (buffer[0] != 5)						{							for (int c = 0; c < p->nClients; c++)							{								if (p->nClientArray[c] == 1 && c != i && p->NeedRecvNotificationPacket[c] <= 2)								{									p->sendall(p->theClient[c],buffer,&retval);									p->NeedRecvNotificationPacket[c]++;								}							}						}						else						{							p->NeedRecvNotificationPacket[(int)buffer[1]]--;						}					}				}			}		}		Sleep(100);	}	return 0;}DWORD WINAPI ServerThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;	char buf[3];	int OldAction = 0;	while (true)	{		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ServerThread returned\n");			return 0;		}		for (int i = 0; i < p->nNumEnemies; i++)		{			p->m_NEnemy.Update();			OldAction = p->m_NEnemy.Action;			p->m_NEnemy.AIMove();			//look for a change of action			if (OldAction != p->m_NEnemy.Action)			{				OldAction = p->m_NEnemy.Action;				//ID for an enemy packet				buf[0] = 3;				//ID of the enemy				buf[1] = (char)i;				//New action of the enemy				buf[2] = (char)(p->m_NEnemy.Action);				//add one for host				for (int i = 0; i < p->nNumPlayers+1; i++)				{					int Size = 3;					int retval = p->sendall(p->theClient,buf,&Size);					p->NeedRecvNotificationPacket++;				}				//TODO snap enemy position with NEnemy position			}		}		Sleep(100);	}	return 0;}int CMultiPlayer::SendText(char *Text){	char TextChar[256];	sprintf(TextChar,"%s",Text);	char SendData[256];	for (int i = 0; i < 256; i++)		SendData = NULL;	SendData[0] = 0;	for (i = 1; i < 256; i++)		SendData = TextChar[i-1];	int Size = 256;	sendall(theSocket,SendData,&Size);	sprintf(ActionText[CurrentArray],Text);	if (CurrentArray != 3)		CurrentArray++;	return 0;}int CMultiPlayer::SendLocation(int ChangeID, int Value){	int buftracker = 0;	char buf[31];	buf[0] = 1;	buftracker++;		char conversionchar[4];		memcpy((void*)conversionchar,(void*)&ClientID,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	//char TestString[512];	//sprintf(TestString,"Sending ArrayPos: %i  Value: %i\n",ChangeID,Value);	//OutputDebugString(TestString);		buf[buftracker] = (char)ChangeID;	buftracker++;	buf[buftracker] = (char)Value;	buftracker++;		//memcpy((void*)conversionchar,(void*)&ClientID,4);	//for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}		memcpy((void*)conversionchar,(void*)&m_Player->m_vPlayer.x,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_Player->m_vPlayer.y,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_Player->m_vPlayer.z,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_Player->m_vPlayerLook.x,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_Player->m_vPlayerLook.y,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_Player->m_vPlayerLook.z,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}		int Size = 31;	sendall(theSocket,buf,&Size);	return 0;}int CMultiPlayer::GetInfoFromServer(){	static char incommingbuffer[512];	static int Tracker = 0;	static int PacketSize = 0;	char buffer[256];	for (int i = 0; i < 256; i++)		buffer = 0;	int retval = 0;		retval = recv(theSocket,buffer,256,0);	if (retval == -1 || retval == 0) return 0;	//slap the buffer we recieved onto the end of incommingbuffer	for (int i = 0; i < retval; i++)	{		incommingbuffer[Tracker] = buffer;		Tracker++;	}	bool bGoodToGo = false;	while (bGoodToGo == false)	{		if (incommingbuffer[0] == 0)			PacketSize = 256;		else if (incommingbuffer[0] == 1)			PacketSize = 31;		else if (incommingbuffer[0] == 2)			PacketSize = 4;		else if (incommingbuffer[0] == 3)			PacketSize = 3;		//char TempString[512];		//sprintf(TempString,"Next type in line to be processed: %i  retval: %i\n",PacketSize,retval);		//OutputDebugString(TempString);		//packet done		if (Tracker >= PacketSize)		{			//OutputDebugString("Complete Packet\n");			//transfer to buffer, snug up incommingbuffer, decrease tracker by packetsize, break			for (int i = 0; i < PacketSize; i++)				buffer = incommingbuffer;			int TrackerTwo = PacketSize;			if (PacketSize != Tracker)			{				//OutputDebugString("Packet overwrote, need to snug\n");				for (int i = 0; i < 512; i++)				{					incommingbuffer = incommingbuffer[TrackerTwo];					TrackerTwo++;					if (TrackerTwo >= Tracker)						break;				}				for (int i = TrackerTwo; i < 512; i++)				{					incommingbuffer = 0;				}			}			Tracker -= PacketSize;			if (Tracker != 0)				bGoodToGo = false;			else				bGoodToGo = true;			//char TestString[512];			//sprintf(TestString,"Tracker: %i\n",Tracker);			//OutputDebugString(TestString);		}		else		{			//OutputDebugString("Incomplete Packet\n");			bGoodToGo = true;			return 0;		}		bool SomethingRecieved = false;				//text 256		if (buffer[0] == 0)		{			//OutputDebugString("Text Recieved\n");			char String[256];			for (int i = 0; i < 255; i++)				String = buffer[i+1];			String[255] = 0;			sprintf(ActionText[CurrentArray],String);			if (CurrentArray != 3)				CurrentArray++;			SomethingRecieved = true;		}		//location 31		else if (buffer[0] == 1)		{			//OutputDebugString("Location Recieved\n");			int ClientNumber;			char buf[4];			int buftracker = 1;			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&ClientNumber,(void*)buf,4);			int ID = (int)buffer[buftracker];			buftracker++;			m_Player[ClientNumber].Mode[ID] = (int)buffer[buftracker];			buftracker++;						//for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			//memcpy((void*)&ClientNumber,(void*)buf,4);						for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayer.x,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayer.y,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayer.z,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayerLook.x,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayerLook.y,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayerLook.z,(void*)buf,4);			SomethingRecieved = true;		}		//client 4		else if (buffer[0] == 2)		{			//OutputDebugString("Client Info Recieved\n");			ClientID = (int)buffer[1];			nNumEnemies = (int)buffer[2];			nNumPlayers = (int)buffer[3];			LoadPlayers();			LoadEnemies();			SomethingRecieved = true;		}		//enemy 3		else if (buffer[0] == 3)		{			//OutputDebugString("Enemy Info Recieved\n");			int EnemyID = (int)buffer[1];			int Action = (int)buffer[2];			//this assumes enemy can only do one move at a time = bad			for (int i = 0; i < 4; i++)				m_Enemy[EnemyID].Mode = 0;			//char TestString[512];			//sprintf(TestString,"ID: %i  ID: %i\n",EnemyID,Action);			//OutputDebugString(TestString);			m_Enemy[EnemyID].Mode[Action] = 1;			SomethingRecieved = true;		}		if (SomethingRecieved == true)		{			//let the server know we recieved it			int Size = 2;			char buf[2];			buf[0] = 5;			buf[1] = (char)ClientID;			sendall(theSocket,buf,&Size);		}	}	return 0;}int CMultiPlayer::sendall(SOCKET s, char *buf, int *len){    int total = 0;        // how many bytes we've sent    int bytesleft = *len; // how many we have left to send    int n;    while(total < *len)	{        n = send(s, buf+total, bytesleft, 0);        if (n == -1) { break; }        total += n;        bytesleft -= n;    }    *len = total; // return number actually sent here    return n==-1?-1:0; // return -1 on failure, 0 on success} int CMultiPlayer::RenderNPlayers(){	//nNumPlayers needs to be sent over packet when changed	if (m_Player == NULL)		return 1;	for (int i = 0; i < nNumPlayers; i++)		m_Player.UpdateAndRender();	return 0;}int CMultiPlayer::RenderEnemies(){	if (m_Enemy == NULL)		return 1;	for (int i = 0; i < nNumEnemies; i++)		m_Enemy.UpdateAndRender();//RenderModel();	return 0;}int CMultiPlayer::LoadPlayers(){	m_Player = new CPlayer[nNumPlayers];	for (int i = 0; i < nNumPlayers; i++)		m_Player.LoadModel("PluggoY.MDL",m_pD3DDevice);	return 0;}int CMultiPlayer::LoadEnemies(){	m_Enemy = new CEnemy[nNumPlayers];	//HARDCODED	m_Enemy[0].LoadModel("PluggoR.MDL",m_pD3DDevice);	m_Enemy[1].LoadModel("Dragon.MDL",m_pD3DDevice);	return 0;}int CMultiPlayer::LoadNPlayers(){	m_NPlayer = new CNPlayer[nNumPlayers];	return 0;}int CMultiPlayer::LoadNEnemies(){	m_NEnemy = new CNEnemy[nNumEnemies];	return 0;}int CMultiPlayer::Cleanup(){	CloseThreads = true;	while (ThreadCount != 0)		Sleep(100);	CloseThreads = false;	if (bHosting == true)	{		for (int i = 0; i < 10; i++)			closesocket(theClient);		closesocket(listeningSocket);		m_NPlayer = NULL;		m_NEnemy = NULL;		delete [] m_NPlayer;		delete [] m_NEnemy;	}	bHosting = false;	m_Player = NULL;	m_Enemy = NULL;	delete [] m_Player;	delete [] m_Enemy;	closesocket(theSocket);	WSACleanup();	return 0;}int CMultiPlayer::GiveD3DDevice(IDirect3DDevice9 *pD3DDevice){	m_pD3DDevice = pD3DDevice;	return 0;}


I've run out of things to try. The debugger won't even tell me where the problem is, even in windowed mode it freezes the computer. Most of the time the graphics card resets, sometimes I have to restart. It has frozen at all the combinations I have tried, even just hosting and connecting to myself. Although I don't think it froze when I just called host and didn't spawn the threads. I don't know, I can't even pinpoint anything, and to tell if it is a problem or not I have to wait so long because it freezes up to a half hour in. Urgh. Advice anyone?

Thank you

Dev578
Red Ant
Red Ant
I'm just having a detailed look at your code and one thing that I have noticed so far is this (this doesn't explain your freeze problem, but it's still an error as far as I can tell).

In your ClientCommunicationsThread() function, you first read the incoming data into a buffer using the recv() function and then (before you've had a chance to look at any of the data you've just received) you just overwrite it with zeros. See my comments in the snippet:

DWORD WINAPI ClientCommunicationsThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;	int retval = 0;	char incommingbuffer[512];	int Tracker = 0;	int PacketSize = 0;	fd_set Input_set, Exc_set;	int s;	timeval timeout;	timeout.tv_sec = 4;	timeout.tv_usec = 4000000;	FD_ZERO(&Input_set);	FD_ZERO(&Exc_set);	for (int i = 0; i < p->nClients; i++)	{		FD_SET(p->theClient, &Input_set);		FD_SET(p->theClient, &Exc_set);	}	while (true)	{		s = select(0, &Input_set, NULL, &Exc_set, &timeout);		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ClientThread returned\n");			return 0;		}		if (s > 0)		{			for (int i = 0; i < p->nClients; i++)			{				if (!FD_ISSET(p->theClient, &Exc_set) && FD_ISSET(p->theClient, &Input_set))				{					char buffer[256];					for (int j = 0; j < 256; i++)						buffer[j] = 0;					// Here you're reading the data into incommingbuffer[].					retval = recv(p->theClient,incommingbuffer,sizeof(incommingbuffer),0);					if (retval == -1 || retval == 0) return 0;					// And here you're zeroing everything you've just received. Surely this isn't what you intended to to!					for (int j = 0; j < retval; j++)					{						incommingbuffer[Tracker] = buffer[j];						Tracker++;					}					bool bGoodToGo = false;					while (bGoodToGo == false)					{						if (incommingbuffer[0] == 0)							PacketSize = 256;						else if (incommingbuffer[0] == 1)							PacketSize = 31;						else if (incommingbuffer[0] == 2)							PacketSize = 4;						else if (incommingbuffer[0] == 3)							PacketSize = 3;						//char TempString[512];						//sprintf(TempString,"Next type in line to be processed: %i  retval: %i\n",PacketSize,retval);						//OutputDebugString(TempString);						//packet done						if (Tracker >= PacketSize)						{							//OutputDebugString("Complete Packet\n");							//transfer to buffer, snug up incommingbuffer, decrease tracker by packetsize, break							for (int j = 0; j < PacketSize; j++)								buffer[j] = incommingbuffer[j];							int TrackerTwo = PacketSize;							if (PacketSize != Tracker)							{								//OutputDebugString("Packet overwrote, need to snug\n");								for (int j = 0; j < 512; j++)								{									incommingbuffer = incommingbuffer[TrackerTwo];									TrackerTwo++;									if (TrackerTwo >= Tracker)										break;								}								for (int j = TrackerTwo; j < 512; j++)								{									incommingbuffer[j] = 0;								}							}							Tracker -= PacketSize;							if (Tracker != 0)								bGoodToGo = false;							else								bGoodToGo = true;							//char TestString[512];							//sprintf(TestString,"Tracker: %i\n",Tracker);							//OutputDebugString(TestString);						}						else						{							//OutputDebugString("Incomplete Packet\n");							//bGoodToGo = true;							//return 0;							break;						}						if (buffer[0] != 5)						{							for (int c = 0; c < p->nClients; c++)							{								if (p->nClientArray[c] == 1 && c != i && p->NeedRecvNotificationPacket[c] <= 2)								{									p->sendall(p->theClient[c],buffer,&retval);									p->NeedRecvNotificationPacket[c]++;								}							}						}						else						{							p->NeedRecvNotificationPacket[(int)buffer[1]]--;						}					}				}			}		}		Sleep(100);	}	return 0;}



P.S. Another thing is that you're accessing shared data across threads without any kind of synchronization, which is a big no-no. For instance, your ThreadCount member can get decremented by all three threads; therefore you should use a critical section to synchronize access to this member.
Red Ant
Red Ant
Actually, the lack of synchronization when accessing the ThreadCount variable might be one the potential causes for your freeze:


int CMultiPlayer::Cleanup(){	CloseThreads = true;	// CAUTION!!! You are not using a critical section to synchronize access to your ThreadCount variable __and__	// the variable is not changed in this loop itself. Therefore the CPU might cache the value of ThreadCount         // and then never look at the actual variable again, which might result in an endless loop of Sleep()'s.	while (ThreadCount != 0)		Sleep(100);	CloseThreads = false;	if (bHosting == true)	{		for (int i = 0; i < 10; i++)			closesocket(theClient);		closesocket(listeningSocket);		m_NPlayer = NULL;		m_NEnemy = NULL;		delete [] m_NPlayer;		delete [] m_NEnemy;	}	bHosting = false;	m_Player = NULL;	m_Enemy = NULL;	delete [] m_Player;	delete [] m_Enemy;	closesocket(theSocket);	WSACleanup();	return 0;}
dev578
dev578
...wow that was dumb of me. Heh, what happened was I copy pasted the code from the client side that did the same thing, made sure that full packets were processed. The server had a different buffer name, so I switched them. It got past me because I am the only one connecting now, and the server doesn't send back information to the client that sent it. Good catch though, thank you.

As for the critical sections and that whole area, there seems to be good info on msdn, I didn't realize this was neccessary. Still, threadcount only gets decreased when the program exits, so I doubt that is causing the freeze, the freeze even happens without spawning the threads. Although, it seems to freeze faster with the threads.

This is quite the problem.

Dev578

Edit: thank you again for your response. I'm looking into critical sections to prevent the potential freeze when the program ends. However, the freeze occurs without ever calling cleanup.

[Edited by - dev578 on August 21, 2006 6:56:19 PM]
Bregma
Bregma
Quote:
Original post by dev578
As for the critical sections and that whole area, there seems to be good info on msdn, I didn't realize this was neccessary. Still, threadcount only gets decreased when the program exits, so I doubt that is causing the freeze, the freeze even happens without spawning the threads. Although, it seems to freeze faster with the threads.


I made a brief glance through your code and it looks like you're doing all kinds of accesses to shared data between threads without regard for serialization. For example, each thread calls p->sendall(), which could be trying to write to the same socket simultanously from three different threads (although without spending more time I can't say if that condition is possible).

From the sound of the lway the lockup happens and since you say it happens even without threads, it sounds more like an array walker is clobbering memory.
Stephen M. Webb
Professional Free Software Developer
dev578
dev578
Sorry to bring this back, but I am still having the same problem. Most of the time my game freezes, sometimes the graphics card looses responsiveness (the screen goes black, and the monitor light flashes just like it does when the computer is off). When run in windowed mode I get no indication of where the crash happens, it just freezes and sometimes lets me get back to VC++, sometimes I need to restart. Here is some slightly revised code (made a recvall function to simplify having to re-write the same code in the client and server, also synchronized access to threadcount).

//cpp
#include "CMultiPlayer.h"CMultiPlayer::CMultiPlayer(){	bHosting = false;	for (int i = 0; i < 5; i ++)	{		for (int i2 = 0; i2 < 256; i2++)		{			ActionText[i2] = 0;				}	}	CloseThreads = false;	ThreadCount = 0;	CurrentArray = 0;	nNumEnemies = 0; nNumPlayers = 0;	m_Player = NULL;	m_NPlayer = NULL;	m_Enemy = NULL;	m_NEnemy = NULL;	for (int i = 0; i < 10; i++)		NeedRecvNotificationPacket = 0;	ServerHold.AmountInHold = 0;	ServerHold.Holding = false;	ServerHold.PacketSizeInHold = 0;	ClientHold.AmountInHold = 0;	ClientHold.Holding = false;	ClientHold.PacketSizeInHold = 0;}CMultiPlayer::~CMultiPlayer(){	Cleanup();}int CMultiPlayer::Host(int Port){	//hardcoded values, we know exactly how much at this point in time	nNumEnemies = 2;	nNumPlayers = 2;	WORD sockVersion;	WSADATA wsaData;	int nret;	//use winsock version 1.1	sockVersion = MAKEWORD(1, 1);	//begin by initializing Winsock	WSAStartup(sockVersion, &wsaData);	//create the listening socket	listeningSocket = socket(AF_INET,	// Go over TCP/IP		SOCK_STREAM,					// This is a stream-oriented socket		IPPROTO_TCP);					// Use TCP rather than UDP	if (listeningSocket == INVALID_SOCKET)	{		//nret = WSAGetLastError();		// Get a more detailed error		//ReportError(nret, "socket()");		// Report the error with our custom function		OutputDebugString("listening socket is invalid\n");		WSACleanup();				// Shutdown Winsock		return 1;			// Return an error value	}	// Use a SOCKADDR_IN struct to fill in address information	SOCKADDR_IN serverInfo;	serverInfo.sin_family = AF_INET;	serverInfo.sin_addr.s_addr = INADDR_ANY;	// Since this socket is listening for connections,							// any local address will do	serverInfo.sin_port = htons(Port);		// Convert integer 8888 to network-byte order							// and insert into the port field	// Bind the socket to our local server address	nret = bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));	if (nret == SOCKET_ERROR) {		//nret = WSAGetLastError();		//ReportError(nret, "bind()");		OutputDebugString("bind failed\n");		WSACleanup();		return 1;	}	// Make the socket listen	nret = listen(listeningSocket, 10);		// Up to 10 connections may wait at any							// one time to be accept()'ed	if (nret == SOCKET_ERROR) {		//nret = WSAGetLastError();		//ReportError(nret, "listen()");		OutputDebugString("listen failed\n");		WSACleanup();		return 1;	}	char ac[80];    	if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)	{        OutputDebugString("could not get host name\n");        return 1;    }    	struct hostent *phe = gethostbyname(ac);    	if (phe == 0)	{        OutputDebugString("gethostbyname failed\n");        return 1;    }	sprintf(ActionText[CurrentArray],"Hosting game with IP's:\n");	if (CurrentArray != 3)		CurrentArray++;    for (int i = 0; phe->h_addr_list != 0; i++)	{        struct in_addr addr;        memcpy(&addr, phe->h_addr_list, sizeof(struct in_addr));        sprintf(ActionText[CurrentArray],"%s\n",inet_ntoa(addr));		if (CurrentArray != 3)			CurrentArray++;    }	LoadNPlayers();	LoadNEnemies();	hExitMutex = CreateMutex(NULL,FALSE,NULL);	if (CreateThread(NULL,0,&ListeningThread,this,0,&ThreadID) != NULL)	{		ThreadCount++;	}	else	{		OutputDebugString("Unable to create ListeningThread\n");		return 1;	}	if (CreateThread(NULL,0,&ClientCommunicationsThread,this,0,&ThreadID) != NULL)	{		ThreadCount++;	}	else	{		OutputDebugString("Unable to create ClientCommunicationsThread\n");		return 1;	}	if (CreateThread(NULL,0,&ServerThread,this,0,&ThreadID) != NULL)	{		ThreadCount++;	}	else	{		OutputDebugString("Unable to create ServerThread\n");		return 1;	}	bHosting = true;	return 0;}int CMultiPlayer::Connect(char* IP, int Port){	WORD sockVersion;	WSADATA wsaData;	int nret;	sockVersion = MAKEWORD(1, 1);	// Initialize Winsock as before	WSAStartup(sockVersion, &wsaData);	// Store information about the server	LPHOSTENT hostEntry;	in_addr iaHost;	if (bHosting == false)		iaHost.s_addr = inet_addr(IP);	else		iaHost.s_addr = inet_addr("127.0.0.1");	hostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET);	if (!hostEntry) {		//nret = WSAGetLastError();		//ReportError(nret, "gethostbyname()");	// Report the error as before		OutputDebugString("gethostbyaddr failed\n");		WSACleanup();		return 1;	}	theSocket = socket(AF_INET,			// Go over TCP/IP			   SOCK_STREAM,			// This is a stream-oriented socket			   IPPROTO_TCP);		// Use TCP rather than UDP	if (theSocket == INVALID_SOCKET) {		//nret = WSAGetLastError();		//ReportError(nret, "socket()");		OutputDebugString("socket failed\n");		WSACleanup();		return 1;	}	// Fill a SOCKADDR_IN struct with address information	SOCKADDR_IN serverInfo;	serverInfo.sin_family = AF_INET;	// At this point, we've successfully retrieved vital information about the server,	// including its hostname, aliases, and IP addresses.  Wait; how could a single	// computer have multiple addresses, and exactly what is the following line doing?	// See the explanation below.	serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);	serverInfo.sin_port = htons(Port);		// Change to network-byte order and							// insert into port field	// Connect to the server	nret = connect(theSocket,		       (LPSOCKADDR)&serverInfo,		       sizeof(struct sockaddr));	if (nret == SOCKET_ERROR) {		//nret = WSAGetLastError();		//ReportError(nret, "connect()");		char ErrorString[512];		sprintf(ErrorString,"connect failed in CMultiPlayer::Connect(), returned error %i\n",WSAGetLastError());		OutputDebugString(ErrorString);		WSACleanup();		return 1;	}	u_long iMode = 1;	ioctlsocket(theSocket, FIONBIO, &iMode);	return 0;}DWORD WINAPI ListeningThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;		// Wait for a client	sockaddr_in addr = {0};	int Size = sizeof(addr);	int ClientNumber = 0;	fd_set Input_set, Exc_set;	int s;	timeval timeout;	timeout.tv_sec = 4;	timeout.tv_usec = 4000000;	FD_ZERO(&Input_set);	FD_ZERO(&Exc_set);	//listening socket used in host and in this thread only, does not need to be synchronized	FD_SET(p->listeningSocket, &Input_set);	FD_SET(p->listeningSocket, &Exc_set);	while (true)	{		// Do the select		s = select(0, &Input_set, NULL, &Exc_set, &timeout);		//needs to be synchronized in all four threads, call it ExitMutex		WaitForSingleObject(p->hExitMutex,INFINITE);		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ListeningThread returned\n");			return 0;		}		ReleaseMutex(p->hExitMutex);				//listening socket only used in this thread and host, no need to sync		if (s > 0 && !FD_ISSET(p->listeningSocket, &Exc_set) && FD_ISSET(p->listeningSocket, &Input_set))		{			//TODO: what if server is full, also need to zero clientarray when someone disconnects			for (int i = 0; i < 10; i++)			{				if (p->nClientArray == 0)				{					ClientNumber = i;					break;				}			}			p->theClient[ClientNumber] = accept(p->listeningSocket,					(sockaddr*)&addr,					&Size);			if (p->theClient[ClientNumber] == INVALID_SOCKET)			{				//nret = WSAGetLastError();				//ReportError(nret, "accept()");				OutputDebugString("accept failed\n");				WSACleanup();				return 1;			}			sprintf(p->ActionText[p->CurrentArray],"Someone Connected");			if (p->CurrentArray != 3)				p->CurrentArray++;			p->nClients++;			p->nClientArray[ClientNumber] = 1;			//send the client a packet with his client ID and the number of players and enemies			char buf[4];			buf[0] = 2;			buf[1] = (char)ClientNumber;			//take out of client packet			buf[2] = (char)p->nNumEnemies;			buf[3] = (char)p->nNumPlayers;					int Size = 4;			p->sendall(p->theClient[ClientNumber],buf,&Size);			p->NeedRecvNotificationPacket[ClientNumber]++;		}		Sleep(100);	}	return 0;}DWORD WINAPI ClientCommunicationsThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;	int retval = 0;	//int Tracker = 0;	//int PacketSize = 0;	fd_set Input_set, Exc_set;	int s;	timeval timeout;	timeout.tv_sec = 4;	timeout.tv_usec = 4000000;	FD_ZERO(&Input_set);	FD_ZERO(&Exc_set);	for (int i = 0; i < p->nClients; i++)	{		FD_SET(p->theClient, &Input_set);		FD_SET(p->theClient, &Exc_set);	}	while (true)	{		s = select(0, &Input_set, NULL, &Exc_set, &timeout);		WaitForSingleObject(p->hExitMutex,INFINITE);		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ClientThread returned\n");			return 0;		}		ReleaseMutex(p->hExitMutex);		if (s > 0)		{			for (int i = 0; i < p->nClients; i++)			{				if (!FD_ISSET(p->theClient, &Exc_set) && FD_ISSET(p->theClient, &Input_set))				{					char buffer[MAXBUFFERSIZE];					for (int j = 0; j < MAXBUFFERSIZE; j++)						buffer[j] = 0;					int len = MAXBUFFERSIZE;					int Packets = 0;					OutputDebugString("About to try to recieve someting in client comm thread\n");					char *bufferptr = buffer;					p->recvall(p->theClient,&bufferptr,&len,&Packets,&p->ServerHold);					OutputDebugString("Recieved Something\n");					int BufferTracker = 0;					int Size = 0;					for (int i = 0; i < Packets; i++)					{						Size = 0;						//text 256						if (buffer[BufferTracker] == 0)						{							Size = 256;						}						//location 31						else if (buffer[BufferTracker] == 1)						{							Size = 31;						}						//client 4						else if (buffer[BufferTracker] == 2)						{							Size = 4;						}						//enemy 3						else if (buffer[BufferTracker] == 3)						{							Size = 3;						}						//notification 2						else if (buffer[BufferTracker] == 5)						{							Size = 2;							p->NeedRecvNotificationPacket[(int)buffer[1]]--;						}						if (Size != 2)						{							for (int c = 0; c < p->nClients; c++)							{								if (p->nClientArray[c] == 1 && c != i && p->NeedRecvNotificationPacket[c] <= 2)								{									p->sendall(p->theClient[c],&buffer[BufferTracker],&Size);									p->NeedRecvNotificationPacket[c]++;								}							}						}						BufferTracker+=Size;					}				}			}		}		Sleep(100);	}	return 0;}DWORD WINAPI ServerThread(LPVOID pParam){	CMultiPlayer *p = (CMultiPlayer*)pParam;	char buf[3];	int OldAction = 0;	//server needs to send info the m_pPlayer, needs to be fixed, revise packet info, easy	while (true)	{		WaitForSingleObject(p->hExitMutex,INFINITE);		if (p->CloseThreads == true)		{			p->ThreadCount--;			//OutputDebugString("ServerThread returned\n");			return 0;		}		ReleaseMutex(p->hExitMutex);		for (int i = 0; i < p->nNumEnemies; i++)		{			p->m_NEnemy.Update();			OldAction = p->m_NEnemy.Action;			p->m_NEnemy.AIMove();			//look for a change of action			if (OldAction != p->m_NEnemy.Action)			{				OldAction = p->m_NEnemy.Action;				//ID for an enemy packet				buf[0] = 3;				//ID of the enemy				buf[1] = (char)i;				//New action of the enemy				buf[2] = (char)(p->m_NEnemy.Action);				//add one for host				for (int i = 0; i < p->nNumPlayers+1; i++)				{					int Size = 3;					int retval = p->sendall(p->theClient,buf,&Size);					p->NeedRecvNotificationPacket++;				}				//TODO snap enemy position with NEnemy position			}		}		Sleep(100);	}	return 0;}int CMultiPlayer::SendText(char *Text){	char TextChar[256];	sprintf(TextChar,"%s",Text);	char SendData[256];	for (int i = 0; i < 256; i++)		SendData = NULL;	SendData[0] = 0;	for (i = 1; i < 256; i++)		SendData = TextChar[i-1];	int Size = 256;	sendall(theSocket,SendData,&Size);	sprintf(ActionText[CurrentArray],Text);	if (CurrentArray != 3)		CurrentArray++;	return 0;}int CMultiPlayer::SendLocation(int ChangeID, int Value){	int buftracker = 0;	char buf[31];	buf[0] = 1;	buftracker++;		char conversionchar[4];		memcpy((void*)conversionchar,(void*)&ClientID,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	//char TestString[512];	//sprintf(TestString,"Sending ArrayPos: %i  Value: %i\n",ChangeID,Value);	//OutputDebugString(TestString);		buf[buftracker] = (char)ChangeID;	buftracker++;	buf[buftracker] = (char)Value;	buftracker++;		//memcpy((void*)conversionchar,(void*)&ClientID,4);	//for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}		memcpy((void*)conversionchar,(void*)&m_pPlayer->m_vPlayer.x,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_pPlayer->m_vPlayer.y,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_pPlayer->m_vPlayer.z,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_pPlayer->m_vPlayerLook.x,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_pPlayer->m_vPlayerLook.y,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}	memcpy((void*)conversionchar,(void*)&m_pPlayer->m_vPlayerLook.z,4);	for (int i = 0; i < 4; i++){buf[buftracker] = conversionchar;buftracker++;}		int Size = 31;	sendall(theSocket,buf,&Size);	return 0;}int CMultiPlayer::GivePlayerPointer(CPlayer *Player){	m_pPlayer = Player;	return 0;}int CMultiPlayer::GetInfoFromServer(){	char buffer[MAXBUFFERSIZE];	for (int i = 0; i < MAXBUFFERSIZE; i++)		buffer = 0;	int SizeOfBuffer = MAXBUFFERSIZE;	int Packets = 0;	char *bufferptr = buffer;	recvall(theSocket,&bufferptr,&SizeOfBuffer,&Packets,&ClientHold);	int BufferOffset = 0;	for (int i = 0; i < Packets; i++)	{		bool SomethingRecieved = false;				//text 256		if (buffer[BufferOffset] == 0)		{			BufferOffset+=256;			//OutputDebugString("Text Recieved\n");			char String[256];			for (int i = 0; i < 255; i++)				String = buffer[i+1];			String[255] = 0;			sprintf(ActionText[CurrentArray],String);			if (CurrentArray != 3)				CurrentArray++;			SomethingRecieved = true;		}		//location 31		else if (buffer[BufferOffset] == 1)		{			BufferOffset+=31;			//OutputDebugString("Location Recieved\n");			int ClientNumber;			char buf[4];			int buftracker = 1;			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&ClientNumber,(void*)buf,4);			int ID = (int)buffer[buftracker];			buftracker++;			m_Player[ClientNumber].Mode[ID] = (int)buffer[buftracker];			buftracker++;						//for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			//memcpy((void*)&ClientNumber,(void*)buf,4);						for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayer.x,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayer.y,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayer.z,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayerLook.x,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayerLook.y,(void*)buf,4);			for (int i = 0; i < 4; i++){buf = buffer[buftracker]; buftracker++;}			memcpy((void*)&m_Player[ClientNumber].m_vPlayerLook.z,(void*)buf,4);			SomethingRecieved = true;		}		//client 4		else if (buffer[BufferOffset] == 2)		{			BufferOffset+=4;			//OutputDebugString("Client Info Recieved\n");			ClientID = (int)buffer[1];			nNumEnemies = (int)buffer[2];			nNumPlayers = (int)buffer[3];			LoadPlayers();			LoadEnemies();			SomethingRecieved = true;		}		//enemy 3		else if (buffer[BufferOffset] == 3)		{			BufferOffset+=3;			//OutputDebugString("Enemy Info Recieved\n");			int EnemyID = (int)buffer[1];			int Action = (int)buffer[2];			//this assumes enemy can only do one move at a time = bad			for (int i = 0; i < 4; i++)				m_Enemy[EnemyID].Mode = 0;			//char TestString[512];			//sprintf(TestString,"ID: %i  ID: %i\n",EnemyID,Action);			//OutputDebugString(TestString);			m_Enemy[EnemyID].Mode[Action] = 1;			SomethingRecieved = true;		}		if (SomethingRecieved == true)		{			//let the server know we recieved it			int Size = 2;			char buf[2];			buf[0] = 5;			buf[1] = (char)ClientID;			sendall(theSocket,buf,&Size);		}	}	return 0;}int CMultiPlayer::sendall(SOCKET s, char *buf, int *len){    int total = 0;        // how many bytes we've sent    int bytesleft = *len; // how many we have left to send    int n;    while(total < *len)	{        n = send(s, buf+total, bytesleft, 0);        if (n == -1) { break; }        total += n;        bytesleft -= n;    }    *len = total; // return number actually sent here    return n==-1?-1:0; // return -1 on failure, 0 on success}int CMultiPlayer::recvall(SOCKET s, char **buf, int *len, int *nFullPackets, HoldStruct *holdstruct){		int PacketSize = 0;	int retval = 0;	//tracker for stepping through buf	int BufTracker = 0;	if (holdstruct->Holding == true)	{		//attempt to get the rest of the packet		retval = recv(s,&((*buf)[holdstruct->AmountInHold]),holdstruct->PacketSizeInHold-holdstruct->AmountInHold,0);		//note, it can't be greater than PacketSize-holdstruct->AmountInHold, only equal or less if we didn't get the		//whole thing				//if we got the rest of the packet		if (retval == holdstruct->PacketSizeInHold-holdstruct->AmountInHold)		{			for (int i = 0; i < holdstruct->AmountInHold; i++)			{				*buf = holdstruct->holdbuffer;			}			BufTracker = holdstruct->PacketSizeInHold;			(*nFullPackets)++;			holdstruct->Holding = false;		}		//if we didn't get the rest of the packet, just adjust holding info		else		{			//holdstruct->AmountInHold and holdstruct->holdbuffer need to be updated			for (int i = 0; i < retval; i++)			{				holdstruct->holdbuffer[holdstruct->AmountInHold+i] = *buf[holdstruct->AmountInHold+i];			}			holdstruct->AmountInHold += retval;			return 0;		}	}		//TODO:size on recv's	retval = recv(s,&((*buf)[BufTracker]),*len - BufTracker,0);		//if it failed or no data was obtained, return	if (retval == SOCKET_ERROR || retval == 0)		return 0;	while (true)	{		//get the packet size of the packet we are dealing with		//text		if ((*buf)[BufTracker] == 0)			PacketSize = 256;		//location		else if ((*buf)[BufTracker] == 1)			PacketSize = 31;		//client		else if ((*buf)[BufTracker] == 2)			PacketSize = 4;		//enemy		else if ((*buf)[BufTracker] == 3)			PacketSize = 3;		BufTracker += PacketSize;		if (retval == BufTracker)		{			//perfect fit			(*nFullPackets)++;			//break the loop			break;		}		else if (retval > BufTracker)		{			//another full packet, still more, loop			(*nFullPackets)++;		}		else		{			//not enough to fill the packet, needs to be stored in holdingbuffer			for (int i = 0; i < PacketSize - (BufTracker - retval); i++)			{				holdstruct->holdbuffer = (*buf)[(BufTracker-PacketSize)+i];			}			holdstruct->AmountInHold = i;			holdstruct->PacketSizeInHold = PacketSize;			holdstruct->Holding = true;		}	}	return 0;}int CMultiPlayer::RenderNPlayers(){	//nNumPlayers needs to be sent over packet when changed	if (m_Player == NULL)		return 1;	for (int i = 0; i < nNumPlayers; i++)		m_Player.UpdateAndRender();	return 0;}int CMultiPlayer::RenderEnemies(){	if (m_Enemy == NULL)		return 1;	for (int i = 0; i < nNumEnemies; i++)		m_Enemy.UpdateAndRender();//RenderModel();	return 0;}int CMultiPlayer::LoadPlayers(){	m_Player = new CPlayer[nNumPlayers];	for (int i = 0; i < nNumPlayers; i++)		m_Player.LoadModel("PluggoY.MDL",m_pD3DDevice);	return 0;}int CMultiPlayer::LoadEnemies(){	m_Enemy = new CEnemy[nNumPlayers];	//HARDCODED	m_Enemy[0].LoadModel("PluggoR.MDL",m_pD3DDevice);	m_Enemy[1].LoadModel("Dragon.MDL",m_pD3DDevice);	return 0;}int CMultiPlayer::LoadNPlayers(){	m_NPlayer = new CNPlayer[nNumPlayers];	return 0;}int CMultiPlayer::LoadNEnemies(){	m_NEnemy = new CNEnemy[nNumEnemies];	return 0;}int CMultiPlayer::Cleanup(){	WaitForSingleObject(hExitMutex,INFINITE);	CloseThreads = true;	ReleaseMutex(hExitMutex);	WaitForSingleObject(hExitMutex,INFINITE);		while (ThreadCount != 0)	{		ReleaseMutex(hExitMutex);		Sleep(100);		WaitForSingleObject(hExitMutex,INFINITE);	}	CloseThreads = false;	ReleaseMutex(hExitMutex);	if (bHosting == true)	{		for (int i = 0; i < 10; i++)			closesocket(theClient);		closesocket(listeningSocket);		m_NPlayer = NULL;		m_NEnemy = NULL;		delete [] m_NPlayer;		delete [] m_NEnemy;	}	bHosting = false;	m_Player = NULL;	m_Enemy = NULL;	delete [] m_Player;	delete [] m_Enemy;	closesocket(theSocket);	WSACleanup();	CloseHandle(hExitMutex);	return 0;}int CMultiPlayer::GiveD3DDevice(IDirect3DDevice9 *pD3DDevice){	m_pD3DDevice = pD3DDevice;	return 0;}


//h
#include <winsock.h>#include <windows.h>#include <stdio.h>#pragma comment(lib,"wsock32.lib")//waits for connectionsDWORD WINAPI ListeningThread(LPVOID pParam);//one for each clientDWORD WINAPI ClientCommunicationsThread(LPVOID pParam);//takes care of enemies and other serverwide stats, sends to clientsDWORD WINAPI ServerThread(LPVOID pParam);//TODO: test what happens if server gets backed up//TODO: critical sections on shared data#define ID_LEFT 0#define ID_RIGHT 1#define ID_DOWN 2#define ID_UP 3#define LARGESTPACKETSIZE 256#define MAXBUFFERSIZE 1024#include "CPlayer.h"#include "CNPlayer.h"#include "CEnemy.h"#include "CNEnemy.h"struct HoldStruct{	char holdbuffer[LARGESTPACKETSIZE-1];	bool Holding;	int PacketSizeInHold;	int AmountInHold;};class CMultiPlayer{public:	//constructor	CMultiPlayer();	//destructor	~CMultiPlayer();	//bool to tell threads to shut down	bool CloseThreads;	//amount of open threads	int ThreadCount;	//set to true on the hosts computer, false on clients	bool bHosting;	//socket used to accept connections	SOCKET listeningSocket;	//a socket for each client	SOCKET theClient[10];	//socket that client uses to send and recieve to server 	SOCKET theSocket;	//called by the host computer, additionally loads network enemy and player info, spawns three threads	int Host(int Port);	//connect to server	int Connect(char *IP, int Port);	//used to send text to server	int SendText(char *Text);	//used to send player location to server	int SendLocation(int ChangeID, int Value);	//used to get information from server	int GetInfoFromServer();	//make sure all information is sent	int sendall(SOCKET s, char *buf, int *len);	//buf returns full packets for processing, len is the length of buf, nFullPackets stores what the name implies	int recvall(SOCKET s, char **buf, int *len, int *nFullPackets, HoldStruct* holdstruct);	//local copy of the d3ddevice	IDirect3DDevice9* m_pD3DDevice;	//function allowing user to give the device	int GiveD3DDevice(IDirect3DDevice9 *pD3DDevice);	//used to render the other players	int RenderNPlayers();	//used to render enemies	int RenderEnemies();		//for everyone	int LoadPlayers();	int LoadEnemies();		//for the host	int LoadNPlayers();	int LoadNEnemies();	//clean everything up, shuts down threads if hosting	int Cleanup();	//used as an ID for threads	DWORD ThreadID;	//number of enemies and players, sent by server, nNumPlayers doesn't count the host	int nNumEnemies, nNumPlayers;	//class pointers for players and enemies, N's only used by server	CPlayer *m_pPlayer;	int GivePlayerPointer(CPlayer *Player);	CPlayer *m_Player;	CNPlayer *m_NPlayer;	CEnemy *m_Enemy;	CNEnemy *m_NEnemy;	//text that main function polls and prints to the edit box	char ActionText[5][256];	//tracker for the action text	int CurrentArray;		//number of clients, variable used by server	int nClients;	//array position = client id, value of one in use, zero not in use	int nClientArray[10];	//one array pos for each client, increased by server everytime it sends something	//client sends a packet back letting the server know it recieved the information	//then the server decreases the value	//used to prevent backups	int NeedRecvNotificationPacket[10];	//the clients ID assigned by the server	int ClientID;	HoldStruct ServerHold;	HoldStruct ClientHold;	HANDLE hExitMutex;};


As a side note, In single player this all works with no freeze, it is just this stuff that is crashing it. I would really appreciate any help, apparently this is harder than I thought.

Thank you in advance

Dev578
abeylin
abeylin
Well, how about first adding checks for errors everywhere? You'll need those if you ever want to debug aproblem.
For example, your 'sendall' returns -1 if it fails, but you don't check for it.

You are calling 'select' but not printing anything in case of error.

Most system functions can fail, you should check for such failures and print error messages.

If that doesn't help, than it's time to put in debug statements - don't just add printf everywhere, make sure it's in some kind of an 'if' statement, or you have a function such as print_for_debug, where you do your debug checking (or you can make that into a define if you are one of those paranoid people that think checking an int slows down your game - then for release version you can remove this part before release). Make sure to not define out your checks for errors though.


Personally I have seen a lot of weird socket problems on Windows 2003 SP1. It doesn't effect Windows 2003 before SP1, I am not sure about Windows XP though (probably latest service packs have same issues, I know future XP patches will have them). Basically Microsoft decided to be smart, and changed behavior of some socket functions in a service pack.
If you have client/server model, than the machine of the client is what matters - that's how Microsoft changed their library.
Officially they have made the change to resist denial of service attacks. I don't want to tell you what they did, because they told me during support call, and they have no official document they would give me, so it could be confidential.
We got into the issue when we were stress testing our server, and clients got some crazy errors during sending and receiving data. For you those errors may manifest as hanging clients.
Red Ant
Red Ant
Quote:
Original post by abeylin
I don't want to tell you what they did, because they told me during support call, and they have no official document they would give me, so it could be confidential.


So what, are they going to sue you because you're sharing information they GAVE you (without asking you to keep it to yourself)? Don't be more catholic than the pope. ;-)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.