C++ Sockets – Webserver (refactored)

C++ Sockets – Webserver (refactored)

Today i made an effort to structurize the code i posted a few days ago in two classes. It is a simple webserver programmed in C++ to learn socket programming. It only supports GET and a few content-types… It also compiles on linux and windows! You can download it here. Remember that the document root is a folder called “www” located in the same directory as the executable. 404.html and 500.html have to be in the same folder as the binary.

#include "Socket.h"
#include "HTTPAnswer.h"

using namespace std;
// Object files needs to link against Winsock2 Lib
#pragma comment(lib, "ws2_32.lib")
#define BUFFER_LENGTH 512 // Length of the recieve buffer - i needed to set it because sizeof(pointer) does always return 4 --> See recieve part

///// MAIN /////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {

	// check parameters
	if(argc < 2 || (string)argv[1] != "-p") {
		cout << "Start Server with -p PORT\n";
		return -1;
	}

	#ifdef _WIN32
		// the path of the executable of this programm is passed as default parameter
		string spfad = argv[0]; // as first default param
		// But the name of the Programm is in the path ( c:\path\to\programm\programm.exe )
		spfad = spfad.substr(0,spfad.find_last_of("\\")); // this deletes everything behind the last backslash
	#else
		// for linux its working as follows
		char pathbuffer[PATH_MAX];
		readlink("/proc/self/exe", pathbuffer, PATH_MAX);
		string spfad = pathbuffer;
		spfad = spfad.substr(0,spfad.find_last_of("/")); // this deletes everything behind the last slash
	#endif
	// this path is needed later on to load html files and so on!

	int			readableSockets,bufferLength=0;
	vector<int>	readableSocketsList;
	char*		buffer;
	Socket*		socket;
	HTTPAnswer	answer;

	// create a new socket
	try {
		socket = new Socket();
		socket->setNonBlocking(true);
		socket->bindToPort(atoi(argv[2]));
	} catch(char* error) {
		cout << error << endl;
		return -1; // exit programm
	}

	while(true) { // server loop
		cout << "Waiting for connections\n";
		try {
			readableSockets = socket->getReadableDescriptors(&readableSocketsList); // get the sockets which are readable now
		} catch(char* e) {
			cout << e << endl;
			break; // terminate server
		}
		cout << readableSockets << " readable sockets found...\n";
		for(unsigned int i=0;i<readableSocketsList.size();i++) { // loop trough all readable sockets and process them
			cout << "processing socket" << readableSocketsList[i] << endl;
			if(readableSocketsList[i] == socket->getDescriptorNumber())
				socket->acceptConnections(); // readable socket is main socket - this means that someone is trying to connect
			else {
				// read data and send back response
				string sbuffer;
				bool boolContinue = true;

				do { // recieve until there is nothing left
					buffer = new char[BUFFER_LENGTH];
					memset(buffer,0,sizeof(buffer));
					try {
						int newBufferLength=socket->receiveData(readableSocketsList[i],buffer,BUFFER_LENGTH); // this is where sizeof(buffer) does not work (at least on ubuntu)
						if (newBufferLength == -1) {
							delete [] buffer;
							break; // everything has been recieved. stop
						}

						bufferLength+=newBufferLength;
						sbuffer.append(buffer,0,newBufferLength);

						if(newBufferLength < BUFFER_LENGTH) // everything is recieved now (needed under ubuntu or it will recieve forever)
							boolContinue = false;
					} catch(char* e) {
						cout << e << endl;
						boolContinue = false;
					}

					delete [] buffer;

				} while(boolContinue);

				if(bufferLength > 0) {
					// now analyse recieved data
					if(sbuffer.substr(0,3) != "GET") {
						answer.status=500;
						answer.filename="/500.html";
					} else {
						answer.filename="/www"+sbuffer.substr(4,sbuffer.find_first_of(" ",4)-4); // this extracts the path to the file
					}

					try {
						socket->sendData(readableSocketsList[i],answer.HTTPResponse(spfad)); // send data back to client
					} catch(char* e) {
						cout << e << endl;
					}
				}

				socket->closeSocket(readableSocketsList[i]); // close connection
			}
		}
	}
	cout << "Shutting down server..." << endl;
	socket->closeSocket();
	delete socket;
}

About the Author

Studying MultimediaTechnology in Salzburg, Austria