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;
}