Why does my MMORPG server crash? :(

Started by
9 comments, last by Afr0m@n 19 years, 1 month ago
main.cpp:
#include "Globals.h"
#include "Network_Init&Shutdown.h"
#include "acceptingThreadProcedure.h"

void main()
{
	int serverSocket;
	bool gQuitFlag = false;

	// For threading, we will need a thread handle.  We will receive this handle when we create the thread
	// and can use this handle any time we need to reference our thread.
	HANDLE threadHandle;

	// a mutex for my shared data
	HANDLE mutexHandle;

	// Finally the file descriptor set (FD_SET).  A FD_SET is simply our list of sockets.
	
	// my master socket set which will be protected by my mutex
	FD_SET masterSet;

	cout << "MMORPG Server\n";
	cout << "=============\n\n";

	serverSocket = startupServerForListening(2751);

	for (;;) 
	{
		if (gQuitFlag) 
		{
			break;
		}

		// check for errors
		if (serverSocket == -1) {
			printf("Network Startup Failed!\nProgram Terminating\n");
			return;
		}

		// create the mutex
		mutexHandle = CreateMutex(NULL, false, NULL);
		if (mutexHandle == NULL) 
		{
			printf("Error creating mutex\n");
			shutdownServer(serverSocket, threadHandle, mutexHandle);
			return;
		}

		// create the thread
		int threadId;
		threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)acceptingThreadProcedure, &serverSocket, 0, (LPDWORD)&threadId);
		if (threadHandle == NULL) 
		{
			printf("Could not start acceptance thread!\n");
			shutdownServer(serverSocket, threadHandle, mutexHandle);
		return;
		}

		Sleep(100);
	}

	return;
}


Globals.h:

#include <iostream.h>
#include <winsock2.h>
#include <stdio.h>
#include <string>

using namespace std;

acceptingThreadProcedure.cpp:
#include "Globals.h"
#include "acceptingThreadProcedure.h"

// -----------------------------------------------------------------------------------
// acceptingThreadProcedure() - a procedure that will accept clients and add their
// sockets to the master file descriptor set

void acceptingThreadProcedure(int* serverSocket, int id, bool gQuitFlag, HANDLE mutexHandle, FD_SET masterSet) 
{
	// copy my socket over to a local variable
	int mySocket = *serverSocket;

	// run forever
	for (;;) {

		// accept a client like normal
		unsigned int clientSocket = accept(mySocket, 0, 0);

		// make sure things are ok
		if (clientSocket == SOCKET_ERROR) {

			// just stop of we received any errors
			printf("Accept Failed!\n");

			// signal to quit the application
			gQuitFlag = true;

			// and quit this thread
			return;

		} else {

			// Everything went well.  We need to take the client socket we received, and add
			// it to our master socket set so the other thread can use it.  Remember though,
			// since our masterSet can be accessed by both threads, we need to make sure
			// that we are not trying to write to it in this thread while at the same time we are trying
			// to read from it in the other.  We need to lock our mutex.  We do this with the function
			// WaitForSingleObject().  We need to give WaitForSingleObject() our mutex handle
			// and a time value to wait.  We will use INFINITE for the time value, because there
			// really isn't anything else our thread needs to do.

			// lock the mutex
			WaitForSingleObject(mutexHandle, INFINITE);

			// add this socket to the master set using our FD_SET() macro
			FD_SET(clientSocket, &masterSet);

			// Now we have to unlock our mutex so that our main thread can also access the data
			// To be absolutely clear on this, here is how it works.  When we call WaitForSingleObject()
			// we have locked the data.  We have control of the mutex until we call ReleaseMutex().
			// If the main thread were to call WaitForSingleObject() before we call ReleaseMutex(),
			// WaitForSingleObject() will block until we unlock the mutex.  At that point, WaitForSingleObject()
			// would immediately get a lock on the mutex, and the main thread would be able to continue.
			// So always lock the mutex, write/read the data, and then unlock the mutex.  This will make
			// everyone trying to access the data very happy.

			// unlock the mutex
			ReleaseMutex(mutexHandle);

			// a quick message
			printf("client on %d connected\n", clientSocket);
			
			id = id + 1;
			
			send(clientSocket, (char*)&id, sizeof(id), 0);
		}
	}
}

acceptingThreadProcedure.h:
void acceptingThreadProcedure(int* serverSocket, int id, bool gQuitFlag, HANDLE mutexHandle, FD_SET masterSet);
NetworkInit&Shutdown.cpp:
#include "Globals.h"
#include "Network_Init&Shutdown.h"

// -----------------------------------------------------------------------------------
// startupServerForListening() - a function to startup winsock, and open a socket for listening

int startupServerForListening(unsigned short port) 
{
	// an error code we will use to get more information about our errors
	int error;

	// the winsock data structure
	WSAData wsaData;

	// startup winsock
	if ((error = WSAStartup(MAKEWORD(2, 2), &wsaData)) == SOCKET_ERROR) {
		printf("Could Not Start Up Winsock!\n");
		return -1;
	}

	// create my socket
	int mySocket = socket(AF_INET, SOCK_STREAM, 0);

	// make sure nothing bad happened
	if (mySocket == SOCKET_ERROR) {
		printf("Error Opening Socket!\n");
		return -1;
	}

	// the address structure
	struct sockaddr_in server;

	// fill the address structure with appropriate data
	server.sin_family = AF_INET;
	server.sin_port = htons(port);
	server.sin_addr.s_addr = INADDR_ANY;

	// and now bind my socket
	if (bind(mySocket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
		printf("Bind Failed!\n");
		closesocket(mySocket);
		return -1;
	}

	// mark my socket for listening
	if (listen(mySocket, 5) == SOCKET_ERROR) {
		printf("Listen Failed!\n");
		closesocket(mySocket);
		return -1;
	}

	printf("Server Started\n");

	return mySocket;
}

// -----------------------------------------------------------------------------------
// shutdownServer() - a function to shutdown a socket and clean up winsock

void shutdownServer(int socket, HANDLE threadHandle, HANDLE mutexHandle) 
{
	// kill my thread and my handle
	WaitForSingleObject(threadHandle, INFINITE);
	CloseHandle(threadHandle);
	CloseHandle(mutexHandle);

	// close our socket
	closesocket(socket);

	// shut down winsock
	WSACleanup();

	printf("Server Shutdown\n");
}
NetworkInit&Shutdown.h:
[source lang = "cpp"]int startupServerForListening(unsigned short port);

void shutdownServer(int socket, HANDLE threadHandle, HANDLE mutexHandle);
When I try to connect my blitz client to this server, the client connects, but I get a windows error from the server:( I can't seem to figure out why! :'( Can anyone tell me why?
_______________________Afr0Games
Advertisement
Well the fact that you're creating a thread and a mutex every 100ms might have something to do with it. Why is that stuff in the middle of a for(;;) loop?


-=[ Megahertz ]=-
-=[Megahertz]=-
What does your debugger tell you? You are using the debugger right??
---------------------http://www.stodge.net
The debugger tells me that the error is here, in the acceptingThreadProcedure function, but I had no idea why;

FD_SET(clientSocket, &masterSet);

Thanks for pointing out about the loop Megahertz;) It's sorted now... but it was a bit odd, because the program didn't take much meory.
_______________________Afr0Games
from what I know, CreateThread needs a certain format for the Thread function
(ie. acceptingThreadProcedure)

DWORD WINAPI ThreadFunc( LPVOID lpParam ) {   //whatever you want to do}main(){hThread = CreateThread(NULL        0,                           // use default stack size          ThreadFunc,                  // thread function         &dwThrdParam,                // argument to thread function         0,                           // use default creation flags         &dwThreadId);                // returns the thread identifier}


your acceptingThreadProcedure is different from the format expected by CreateThread. When the thread calls the acceptingThreadProcedure, it calls
using 1 parameter but your function expects 5, possibly using corrupt values for the other variables.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/threadproc.asp
---------------Magic is real, unless declared integer.- the collected sayings of Wiz Zumwalt
Posting the entire source and asking "Why does this crash" is not a reasonable request.

This is a discussion and help forum not a free "we-debug-your-code-for-you" service.

Mark
Thanks yapposai:) I'll look into it.

@markr: I usually debug my code. However, because I was so tired yesterday and was going to bed, I'm willing to admit that I forgot it. In this case, it doesn't matter though, cos I didn't understand the debugger anyway. I'm quite a newbie when it comes to C++, but I try to be as independent as possible:)
_______________________Afr0Games
You admit you're a newbie, and you "forgot" to use the debugger, yet you post a MMORPG question in the Networking forum?

Here's a bit of advice that will make you a better programmer (which I hope is your end goal):

1) Use the appropriate forum for your skill level. In this case, "for beginners".

2) Whenever you add new code, or change old code, put a breakpoint right at that code in the editor. When you next run the program, it will break at all the changed/added places. Use single-step to step through the code, verifying that it does what you think it should do.

3) Don't program when you're too tired, and if you have a bug you can't beat, let it go until the next day. If you still can't beat it the next day (using debuggers, tracing, commenting out code, and unit testing as usual), THEN ask for help in a public forum.

I'm moving this thread to For Beginners. Good luck!
enum Bool { True, False, FileNotFound };
Beginner + MMORPG server = You're in over your head, drowning is eminent.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other player's eyes with his King.
I wonder how long it takes before someone is going to post the MMORPG comic...

Afr0m@n: If I were you, I would stop spawning 1 thread per client or whatever. Just code the main "Check for new connections and update all clients" piece into a function.

I'm building a MUD at the moment, and I got all my ServerController code in a class with the same name. It checks for new connections and then proceeds to update all clients. All of it runs in 1 single thread.

Using threads should have a purpose, not just to look cool, professional or just for the sake of it. And that's what you're doing, using threads just to use them. Threads make it alot more complicated than the task already is.

If you're going to stick with Threads/Mutexes and what not, shove them into a class(Such as a CMutex which has a Lock(), Unlock() and IsLocked() function).

Same accounts for the sockets. Stuff them into a class(Or multiple for protocol sake) to be able to manage them better.

If I were you, I'd stick with writing a few simple network games first, such as Networked Tetris, A networked bomberman clone, etc.

Take my word for it, building a MUD is ALOT of HARD work, an MMORPG is about 10 times as hard.

Toolmaker

This topic is closed to new replies.

Advertisement