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_NUMthreads (epollfdandlistenfdwill be passed as arguments). Then both the main thread and the child threads will serve as workers. -
All the workers call
epoll_waiton 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 callaccepton it and addconnfd(return value ofaccept) to the interest list. -
Else it is a
connfd.readon 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_tis defined as:typedef enum REQUEST_STATUS { Reading, Writing, Ended } req_status_t;
Next time when
epoll_waitget events on this fd, the server will continue on the request. -
After complete reading, connfd will enter status
Writing. IfsendfilecauseEAGAIN, andleft > 0, it means that writing end is temporily unavailable. I have to save the status,EPOLL_CTL_MODto change its trigger events toEPOLLOUT | EPOLLET. And continue the writing next time.
-
-
While calling
readorsendfileon a nonblocking descriptor, only when you get a-1return value anderrno == EAGAIN || errno == EWOULDBLOCK(no data left) or a0return value (EOF detected), it means that the reading is done.You may get a
0return 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_waitis generally thread-safe. ET also overperfoms LT. -
epoll_data_t.datais a union. Itsptrfield 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
writeing half of the content, sleeping for 10 seconds, andwriteing 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:
