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;
}
🔒 Locked
🎮 Unity
Multithreading Problem
Advertisement
Original Post
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. Any ideas? Thank you Dev578 [Edited by - dev578 on August 28, 2006 9:49:03 AM]
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....
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....
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.
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
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
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.
You need Sleep(1) or something in each of the while(true) threads to allow the others to get a slice of time.
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
//cpp file
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
//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
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:
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.
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.
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;}
...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]
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]
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.
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
//h
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
//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
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.
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.
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.
Advertisement