You might wonder how web servers "serve" files, and how they deliver files that piece together a website that shows on your screen. Well, knowing how a web server is implemented is certainly a good way to "learn by building".
Although implementing a web server in C sounds very basic and detailed at a ground level, but it might help you better understand how HTTP works, and how servers actually interacts with clients. Let's get started!
How Socket Works
Before building the web server, you need to know how a "socket" works.
If a program (or process) running on a host is like a house, a socket is like a door that lets mails go in and out. When a person in the house receives or delivers a letter, he or she can be agnostic to how the mail is being delivered in the outside world.
Using the socket network interface, our web server can use a set of functions from C standard package <sys/socket.h>
and let our server "talk to" clients over the internet. Those clients are also using sockets to talk to us, so socket is basically like a consesus on how to talk with each other over the internet.
Set up Server Socket
To kick things off, I started by creating and configuring a socket for the server. I configured a few things:
- AF_INET: use IPv4 (vs IPv6)
- SOCK_STREAM: use TCP (vs UDP)
- INADDR_ANY: the server accepts connections from any network interface
bind()
binds the socket to a port (I'm using 8080 here) means the socket will listen to any clients trying to connect to the port 8080. listen()
takes the maximum number of pending connections (I set it to 10).
Now, my server is all set up and ready to accept incoming client connections.
Handle Client Connections
With the server up and running, the next step is to handle incoming client connections. I used an infinite loop to continuously listen for new clients.
When a client connects, the server accepts the connection and creates a new thread to handle the client's HTTP request. This way, the server can handle multiple clients concurrently.
I defined a function handle_client()
that handles an incoming request from a client. I only implemented GET here since it shows a good case on how server "serve" files to clients.
handle_client()
receives the request data, extracts the requested file name, and decodes the URL (e.g. convert %20 to space). Then, it identifies the file extension, builds an HTTP response containing the requested file, and sends it back to the client.
Build HTTP Response
Inside handle_client()
, I defined another function build_http_response()
that constructs an HTTP response, containing a header and the actual file.
The function starts by building an HTTP header with the appropriate MIME type based on the file extension (e.g. if requesting.jpg, MIME type is image/jpeg). If the file doesn't exist, the function creates a 404 Not Found response. Otherwise, it retrieves the file's size and appends the 200 OK header to the response buffer.
Next, it reads the file content and appends it to the response buffer. The response buffer returned back to handle_client()
is sent back to the client .I set BUFFER_SIZE to 1MB, meaning the server can handle any HTTP response with size up to 1MB.
A Working HTTP Server!
To wrap up, I hope this tutorial helps you better understand how servers actually interacts with clients.
Here is the github repo for the full code.
🌟 Special Giveaway
CodeCrafters has a coding challenge that guides you to build a HTTP server step-by-step in any language. Join using my referral link to get 40% off 🚀
Top comments (13)
That's not C++, that's C, it doesn't even compile with a C++ compiler (in the Github project it uses gcc instead of g++)
To make it a valid C++ code you just to change some implicit conversion, like for exemple when you use malloc :
int* fd = malloc(sizeof(int));
In C++ you need to explicitly cast it:
int* toto = static_cast<int*>(malloc(sizeof(int)));
To make it more idiomatic C++ you would need to:
Thanks for the correction. I have no idea why I said C++ while I was using gcc - I was confusing it with another project I'm working on.
Don't worry it happens to everyone sometimes haha
I saw that you updated the title, don't forget to update the tag :)
WHY the heck do we need theese far-fetched idioms and features just to make a simple action and write minimalistic code?
Far fetched? You can see them for example in the core guidelines and a lot of codebase using modern c++ recommend to use similar idioms.
These idioms are not here just because someone said so, but because they make the code safer and more maintainable.
Also, I never said they had to apply them, but the code would almost the same structure and without same it was C code, not C++. Turns out that the author meant C and not C++ and he corrected the article.
Thank you for your efforts in writing this post. I just have one suggestion though. Instead of converting the code to images and then posting them. you can wrap your code in code fences. i.e put you between three backticks(`) followed by the name of the language, and end it with three more backticks
This allows the code to be copied directly from the post.
Other than that I liked your post.
Thank You.
Hey Adwaith, thanks for the suggestion! I tried using code blocks but the long code blocks would make the post unreadable, and the syntax highlighting in pictures make code easier to read
I understand. Thanks for the post.. It was helpful.
i would use epoll_wait and non blocking sockets
Good suggestion :)
I need full code can you please send me
github.com/JeffreytheCoder/Simple-...
Isn't the code slow because it's synchronous?