Skip to content

Commit 6e0a92a

Browse files
committed
HTTPServer is now on separate thread. Fixed issues with select blocking.
1 parent 8b0eb99 commit 6e0a92a

File tree

4 files changed

+156
-99
lines changed

4 files changed

+156
-99
lines changed

HTTPServer.cpp

Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
* Initialize state and server variables
66
*/
77
HTTPServer::HTTPServer() {
8+
canRun = false;
9+
thread = NULL;
10+
811
listenSocket = INVALID_SOCKET;
912
memset(&serverAddr, 0, sizeof(serverAddr)); // clear the struct
10-
keepRunning = false;
1113

1214
// Create a resource manager managing the VIRTUAL base path ./
1315
resMgr = new ResourceManager("./", true);
@@ -28,22 +30,28 @@ HTTPServer::~HTTPServer() {
2830
if(listenSocket != INVALID_SOCKET)
2931
closeSockets();
3032
delete clientMap;
33+
34+
if(thread != NULL)
35+
delete thread;
3136
}
3237

3338
/**
34-
* Init Socket
39+
* Start Server
3540
* Initialize the Server Socket by requesting a socket handle, binding, and going into a listening state
3641
*
3742
* @param port Port to listen on
3843
* @return True if initialization succeeded. False if otherwise
3944
*/
40-
bool HTTPServer::initSocket(int port) {
45+
void HTTPServer::start(int port) {
4146
// Create a handle for the listening socket, TCP
4247
listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
4348
if(listenSocket == INVALID_SOCKET) {
4449
cout << "Could not create socket!" << endl;
45-
return false;
50+
return;
4651
}
52+
53+
// Set socket as non blocking
54+
fcntl(listenSocket, F_SETFL, O_NONBLOCK);
4755

4856
// Populate the server address structure
4957
serverAddr.sin_family = AF_INET; // Family: IP protocol
@@ -53,23 +61,47 @@ bool HTTPServer::initSocket(int port) {
5361
// Bind: Assign the address to the socket
5462
if(bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
5563
cout << "Failed to bind to the address!" << endl;
56-
return false;
64+
return;
5765
}
5866

5967
// Listen: Put the socket in a listening state, ready to accept connections
6068
// Accept a backlog of the OS Maximum connections in the queue
6169
if(listen(listenSocket, SOMAXCONN) != 0) {
6270
cout << "Failed to put the socket in a listening state" << endl;
63-
return false;
71+
return;
6472
}
6573

6674
// Add the listening socket to the master set and the largest FD is now the listening socket
6775
FD_SET(listenSocket, &fd_master);
6876
fdmax = listenSocket;
6977

70-
keepRunning = true;
78+
// Set select to timeout at 50 microseconds
79+
timeout.tv_sec = 0;
80+
timeout.tv_usec = 50;
81+
82+
cout << "Server started. Listening on port " << port << "..." << endl;
83+
84+
// Spawn thread
85+
canRun = true;
86+
thread = new boost::thread(boost::ref(*this));
87+
}
88+
89+
/**
90+
* Stop
91+
* Signal the server thread to stop running and shut down
92+
*/
93+
void HTTPServer::stop() {
94+
runMutex.lock();
95+
canRun = false;
96+
runMutex.unlock();
97+
98+
if(thread != NULL)
99+
thread->join();
71100

72-
return true;
101+
// Safely shutdown the server and close all open connections and sockets
102+
closeSockets();
103+
104+
cout << "Server shutdown!" << endl;
73105
}
74106

75107
/**
@@ -107,6 +139,9 @@ void HTTPServer::acceptConnection() {
107139
clfd = accept(listenSocket, (sockaddr*)&clientAddr, (socklen_t*)&clientAddrLen);
108140
if(clfd == INVALID_SOCKET)
109141
return;
142+
143+
// Set socket as non blocking
144+
fcntl(clfd, F_SETFL, O_NONBLOCK);
110145

111146
// Instance Client object
112147
Client *cl = new Client(clfd, clientAddr);
@@ -126,50 +161,49 @@ void HTTPServer::acceptConnection() {
126161
}
127162

128163
/**
129-
* Run Server
130-
* Main server loop where the socket is initialized and the loop is started, checking for new messages or clients to be read with select()
131-
* and handling them appropriately
164+
* Server Process
165+
* Main server processing function that checks for any new connections or data to read on
166+
* the listening socket
132167
*/
133-
void HTTPServer::runServer(int port) {
134-
// Initialize the socket and put it into a listening state
135-
if(!initSocket(port)) {
136-
cout << "Failed to start server." << endl;
137-
return;
138-
}
139-
140-
cout << "Server started. Listening on port " << port << "..." << endl;
141-
142-
// Processing loop
143-
while(keepRunning) {
144-
usleep(1000);
168+
void HTTPServer::operator() () {
169+
bool run = true;
170+
int sret = 0;
171+
172+
while(run) {
173+
// Update the running state
174+
runMutex.lock();
175+
run = canRun;
176+
runMutex.unlock();
145177

146-
// Copy master set into fd_read for processing
147-
fd_read = fd_master;
148-
149-
// Populate read_fd set with client descriptors that are ready to be read
150-
if(select(fdmax+1, &fd_read, NULL, NULL, NULL) < 0) {
151-
//printf("select failed!");
152-
continue;
153-
}
154-
155-
// Loop through all descriptors in the read_fd set and check to see if data needs to be processed
156-
for(int i = 0; i <= fdmax; i++) {
157-
// Socket i isn't ready to be read (not in the read set), continue
158-
if(!FD_ISSET(i, &fd_read))
159-
continue;
160-
161-
// A new client is waiting to be accepted on the listenSocket
162-
if(listenSocket == i) {
163-
acceptConnection();
164-
} else { // The descriptor is a client
165-
Client *cl = getClient(i);
166-
handleClient(cl);
167-
}
168-
}
169-
}
170-
171-
// Safely shutdown the server and close all open connections and sockets
172-
closeSockets();
178+
// Copy master set into fd_read for processing
179+
fd_read = fd_master;
180+
181+
// Populate read_fd set with client descriptors that are ready to be read
182+
// return values: -1 = unsuccessful, 0 = timeout, > 0 - # of sockets that need to be read
183+
sret = select(fdmax+1, &fd_read, NULL, NULL, &timeout);
184+
if(sret > 0) {
185+
// Loop through all descriptors in the read_fd set and check to see if data needs to be processed
186+
for(int i = 0; i <= fdmax; i++) {
187+
// Socket i isn't ready to be read (not in the read set), continue
188+
if(!FD_ISSET(i, &fd_read))
189+
continue;
190+
191+
// A new client is waiting to be accepted on the listenSocket
192+
if(listenSocket == i) {
193+
acceptConnection();
194+
} else { // The descriptor is a client
195+
Client *cl = getClient(i);
196+
handleClient(cl);
197+
}
198+
}
199+
} else if(sret < 0) {
200+
cout << "Select failed!" << endl;
201+
break;
202+
} else { // Timeout
203+
// Yield rest of time slice to CPU
204+
boost::this_thread::yield();
205+
}
206+
}
173207
}
174208

175209
/**

HTTPServer.h

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33

44
#include <map>
55
#include <string>
6+
#include <boost/thread.hpp>
67

78
#include <time.h>
89
#include <unistd.h>
910
#include <sys/types.h>
1011
#include <sys/socket.h>
12+
#include <sys/time.h>
1113
#include <netinet/in.h>
1214
#include <arpa/inet.h>
1315
#include <netdb.h>
16+
#include <fcntl.h>
1417

1518
#include "Client.h"
1619
#include "HTTPRequest.h"
@@ -21,29 +24,24 @@
2124
#define INVALID_SOCKET -1
2225

2326
class HTTPServer {
24-
// Private variables
27+
// Network
2528
SOCKET listenSocket; // Descriptor for the listening socket
26-
bool keepRunning; // Flag when true will keep the main loop of the server running
2729
struct sockaddr_in serverAddr; // Structure for the server address
2830
fd_set fd_master; // Master FD set (listening socket + client sockets)
2931
fd_set fd_read; // FD set of sockets being read / operated on
32+
struct timeval timeout; // Select timeout
3033
int fdmax; // Max FD number (max sockets hanlde)
3134
map<SOCKET, Client*> *clientMap; // Client map, maps Socket descriptor to Client object
3235

36+
// Resources / File System
3337
ResourceManager *resMgr;
38+
39+
// Threading
40+
bool canRun;
41+
boost::thread* thread;
42+
boost::mutex runMutex;
3443

3544
// Private methods
36-
bool initSocket(int port = 80);
37-
void closeSockets();
38-
39-
public:
40-
HTTPServer();
41-
~HTTPServer();
42-
void runServer(int port=80);
43-
void stopServer() {
44-
keepRunning = false;
45-
}
46-
4745
void acceptConnection();
4846
Client *getClient(SOCKET clfd);
4947
void disconnectClient(Client* cl);
@@ -54,6 +52,15 @@ class HTTPServer {
5452
void handleRequest(Client* cl, HTTPRequest* req);
5553
HTTPResponse* handleHead(Client* cl, HTTPRequest *req);
5654
HTTPResponse* handleGet(Client* cl, HTTPRequest *req);
55+
void closeSockets();
56+
57+
public:
58+
HTTPServer();
59+
~HTTPServer();
60+
61+
void start(int port);
62+
void stop();
63+
void operator() ();
5764
};
5865

5966
#endif

Makefile

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,48 @@
11
# Makefile for httpserver
22
# (C) Ramsey Kant 2011-2012
33

4-
CC = g++
4+
CC = g++
55
OBJS = ByteBuffer.o Client.o HTTPMessage.o HTTPRequest.o HTTPResponse.o HTTPServer.o main.o Resource.o ResourceManager.o
66
# Debug Flags
77
DEBUGFLAGS = -g -O0 -fpermissive -Wall
88
# Production Flags
9-
PRODFLAGS = -Wall -O3
10-
# Active Flags
11-
FLAGS = $(DEBUGFLAGS)
9+
PRODFLAGS = -Wall -O3
10+
# Active Flags
11+
FLAGS = -Iinclude/ $(DEBUGFLAGS)
12+
LINK = -lpthread -lboost_thread-mt
1213

1314
all: $(OBJS)
14-
$(CC) $(FLAGS) *.o -o bin/httpserver
15-
15+
$(CC) $(FLAGS) *.o -o bin/httpserver $(LINK)
16+
1617
ByteBuffer.o: ByteBuffer.cpp
17-
$(CC) $(FLAGS) -c ByteBuffer.cpp -o $@
18-
18+
$(CC) $(FLAGS) -c ByteBuffer.cpp -o $@
19+
1920
Client.o: Client.cpp
20-
$(CC) $(FLAGS) -c Client.cpp -o $@
21-
21+
$(CC) $(FLAGS) -c Client.cpp -o $@
22+
2223
HTTPMessage.o: HTTPMessage.cpp
23-
$(CC) $(FLAGS) -c HTTPMessage.cpp -o $@
24-
24+
$(CC) $(FLAGS) -c HTTPMessage.cpp -o $@
25+
2526
HTTPRequest.o: HTTPRequest.cpp
26-
$(CC) $(FLAGS) -c HTTPRequest.cpp -o $@
27-
27+
$(CC) $(FLAGS) -c HTTPRequest.cpp -o $@
28+
2829
HTTPResponse.o: HTTPResponse.cpp
29-
$(CC) $(FLAGS) -c HTTPResponse.cpp -o $@
30-
30+
$(CC) $(FLAGS) -c HTTPResponse.cpp -o $@
31+
3132
HTTPServer.o: HTTPServer.cpp
32-
$(CC) $(FLAGS) -c HTTPServer.cpp -o $@
33-
33+
$(CC) $(FLAGS) -c HTTPServer.cpp -o $@
34+
3435
main.o: Testers.h main.cpp
35-
$(CC) $(FLAGS) -c main.cpp -o $@
36-
36+
$(CC) $(FLAGS) -c main.cpp -o $@
37+
3738
Resource.o: Resource.cpp
38-
$(CC) $(FLAGS) -c Resource.cpp -o $@
39-
39+
$(CC) $(FLAGS) -c Resource.cpp -o $@
40+
4041
ResourceManager.o: ResourceManager.cpp
4142
$(CC) $(FLAGS) -c ResourceManager.cpp -o $@
4243

4344
.PHONY: clean
4445
clean:
4546
rm -f bin/httpserver
46-
rm -f bin/*.o
47+
rm -f *.o
4748

0 commit comments

Comments
 (0)