A simple yet high performance web server written with epoll and pure c.
Type in folder /files
make
./server
Recommend to read this post of me.
Concurrency of this web server was implemented by using thread pool (Posix thread) and epoll.
-
The main thread finish initializing work (e.g., configing network, listening on socket).
-
The main thread creates an epoll instance and adds the listen file descriptor to the interest list of the epoll instance (Edge Triggered).
-
The main thread creates
THREAD_NUM
threads (epollfd
andlistenfd
will be passed as arguments). Then both the main thread and the child threads will serve as workers. -
All the workers call
epoll_wait
on the epoll instance. -
When any event is caught, the worker thread that gets it will do:
-
Check if it's
listenfd
. If It islistenfd
, then callaccept
on it and addconnfd
(return value ofaccept
) to the interest list. -
Else it is a
connfd
.read
on it. If the header transport is done (a blank new line detected), then respond to it. Else store the status atevent.data.ptr
.The status is defined as:
typedef struct HttpStatus { int connfd; // connection file descriptor char *header; // http header read, malloced with `MAX_HEADER` size size_t readn; // number of bytes read FILE *file; // file to send size_t left; // number of bytes left to send req_status_t req_status; } http_status_t;
While
req_status_t
is defined as:typedef enum REQUEST_STATUS { Reading, Writing, Ended } req_status_t;
Next time when
epoll_wait
get events on this fd, the server will continue on the request. -
After complete reading, connfd will enter status
Writing
. Ifsendfile
causeEAGAIN
, andleft > 0
, it means that writing end is temporily unavailable. I have to save the status,EPOLL_CTL_MOD
to change its trigger events toEPOLLOUT | EPOLLET
. And continue the writing next time.
-
-
While calling
read
orsendfile
on a nonblocking descriptor, only when you get a-1
return value anderrno == EAGAIN || errno == EWOULDBLOCK
(no data left) or a0
return value (EOF detected), it means that the reading is done.You may get a
0
return value when:- Client close the connection. At this case the client will send a EOF implicitly.
- Client shutdown the writing end of the connection.
- Client explicitly send a EOF to signal the server it has finished writing.
-
You can only use Edge Triggered mode in multi-thread environments (when you share an epoll instance among threads) to prevent spurious wake-ups.
Why I share an epoll instance among threads: Because this is more performant. And
epoll_wait
is generally thread-safe. ET also overperfoms LT. -
epoll_data_t.data
is a union. Itsptr
field is designed to store session state. You should malloc status when needed and assign it to theptr
, and free it after you have done responding to it. -
Notice that writing to connfd (by calling
sendfile
) is also non-blocking. -
USE EPOLLONESHOT in multithreaded environment.
- The server operates smoothly when network is bad. I simulate this situation by
write
ing half of the content, sleeping for 10 seconds, andwrite
ing the remaining half. - The performance, concurrency and availability seems well on my dual core virtual machine.
- Basic access control by comparing the absolute path of the request file and workspace of the server.
Test on my dual core ubuntu virtual machine: