Building HTTP Server From Scratch in C++
Building HTTP Server From Scratch in C++
Scratch in C++
Osamudiamen Azamegbe Follow 12 min read · Feb 5, 2022
535 4
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 1 of 35
:
building an HTTP server from scratch using C++ (Just as the title
suggests. Weird, right?). I will be doing my best to arm you with all
the necessary tools and knowledge you will need to be able to write
your server, even if your design varies from the example shown in
this demo. 5 + 4 = 9, same as 6 and 3. So go forth and get crazy with
it :). The implementation of an HTTP server described here is also
language-agnostic; I just happened to choose C++.
Let’s begin.
Now that we have this HTTP term out of the way, an HTTP server is
simply a computer that serves data over a network via HTTP. It is
the basic building block of a web server (and probably its most
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 2 of 35
:
important component).
The response sent by the HTTP server may also include data from
files (for example, HTML documents) stored on the webserver.
Now that we’re done with the base introduction — let’s dive right
into the design for our server.
High-level design
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 3 of 35
:
High-level design
We will be using TCP (Transmission Control Protocol) to
implement our HTTP server. TCP is the most common protocol
used for HTTP servers, but there are others — we won’t be talking
about them in this article.
reads the data (Request) sent from the client over the network
connection
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 4 of 35
:
Implementation
Alright then, let’s jump right into our code!
library as an alternative.
You can head over to the GitHub repository if you would like to
inspect the code as you follow along.
OOP design
The TcpServer class will be a layer of abstraction for all of our
server code. Before we get into some of the names I have chosen
for the classes, methods, etc., let us just go over one of my favourite
quotes;
There are only two hard things in Computer Science: cache invalidation
and naming things.
— Phil Karlton
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 5 of 35
:
We will be creating three main files for our code:
http_tcpServer_linux.h
http_tcpServer_linux.cpp
server_linux.cpp
Note: I will be using the Linux version of the code as a main point of
reference. If you are following along with the windows version, I will let
you know when we get to parts that differ.
#ifndef INCLUDED_HTTP_TCPSERVER_LINUX
#define INCLUDED_HTTP_TCPSERVER_LINUX
namespace http
{
class TcpServer
{
public:
TcpServer();
~TcpServer();
private:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 6 of 35
:
};
} // namespace http
#endif
#include <http_tcpServer_linux.h>
namespace http
{
TcpServer::TcpServer()
{
}
TcpServer::~TcpServer()
{
}
} // namespace http
#include <http_tcpServer_linux.h>
int main()
{
using namespace http;
TcpServer server = TcpServer();
return 0;
}
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 7 of 35
:
This is what our files look like at the moment:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 8 of 35
:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 9 of 35
:
Creating a TCP/IP socket
To create a TCP socket we will be using the “ socket” system API.
The socket API takes three parameters and is called as seen
below:
The windows version is more or less the same. From the definition,
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 10 of 35
:
it returns a SOCKET type - which is a type definition for an unsigned
integer (you can learn more about signed and unsigned integers
here).
domain
This argument specifies the communication domain. It represents
the protocol family that the socket will belong to. For a TCP/IP
socket, we will be using the IPv4 Internet protocols defined by the
AF_INET domain.
type
This argument describes the type of communication structure the
socket will allow for this protocol family. We will be using
SOCK_STREAM - to allow for reliable, full-duplex byte streams.
protocol
This specifies the particular protocol the socket will use from the
given family of protocols that support the chosen type of
communication. For the AF_INET family, there is only one protocol
that supports SOCK_STREAM . We will be setting this parameter to 0.
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 11 of 35
:
m_socket = socket(AF_INET, SOCK_STREAM, 0);
You can learn more about this API by checking the Linux man-
pages.
We will wrap the code for creating our socket in a function called
startServer() . This function will be called in our TcpServer class
constructor.
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 12 of 35
:
//Linux
int TcpServer::startServer()
{
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_socket < 0)
{
exitWithError("Cannot create socket");
return 1;
}
return 0;
}
//Windows
int TcpServer::startServer()
{
if (WSAStartup(MAKEWORD(2, 0), &m_wsaData) != 0)
{
exitWithError("WSAStartup failed");
}
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_socket < 0)
{
exitWithError("Cannot create socket");
return 1;
}
return 0;
}
Closing a socket
When our TcpServer gets cleaned up (the destructor function,
~TcpServer() , is called) we want to make sure we also close the
socket we’ve created for our server. It is good practice to remember
to clean up after yourself, especially when system resources can be
freed up. On Linux, we can do this using the close() system call.
We use the c losesocket() function on windows. We will also need
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 13 of 35
:
to call the WSACleanup() function for the Windows version. This
function terminates all Windows socket operations on all threads
for the program.
//Linux
void TcpServer::closeServer()
{
close(m_socket);
close(m_new_socket);
exit(0);
}
//Windows
void TcpServer::closeServer()
{
closesocket(m_socket);
closesocket(m_new_socket);
WSACleanup();
exit(0);
}
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 14 of 35
:
exitWithError() - for logging an error message and exiting the
process.
In windows, you can retrieve the last error message associated with
your socket threads with the WSAGetLastError() function.
//Linux
void log(const std::string &message)
{
std::cout << message << std::endl;
}
void exitWithError(const std::string &errorMessage)
{
log("ERROR: " + errorMessage);
exit(1);
}
//Windows
void log(const std::string &message)
{
std::cout << message << std::endl;
}
void exitWithError(const std::string &errorMessage)
{
std::cout << WSAGetLastError() << std::endl;
log("ERROR: " + errorMessage);
exit(1);
}
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 15 of 35
:
Open in app Sign up Sign in
Search Write
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 16 of 35
:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 17 of 35
:
The following files are slightly different in the Windows version:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 18 of 35
:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 19 of 35
:
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 20 of 35
:
Naming the socket
This involves assigning the socket to an IP address and a port.
struct sockaddr_in {
short sin_family; // e.g. AF_INET
unsigned short sin_port; // e.g. htons(8080)
struct in_addr sin_addr; // see struct in_addr,
below
char sin_zero[8]; // zero this if you want
to
};
struct in_addr {
unsigned long s_addr;
};
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 21 of 35
:
sin_family
This is the socket family. In our case, that is AF_INET .
sin_port
The port to be used by our server. There are a couple of reserved
ports and TCP/IP servers typically use port 80, but we will use 8080
in our code. We will need to call htons() to ensure that the port is
stored in network byte order.
sin_addr
This is the IP address we will be using for the server. There are a
few usually a couple of IP addresses available per network
interface for your machine (usually one or two of them). We will be
using 0.0.0.0 which is a default IP address that lets the operating
system choose anyone it wants. The member variable sin_addr
sin_zero
This member variable has no “real” utility in the sockaddr_in
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 22 of 35
:
however common practice to zero-out the bytes in this field when
not used.
The first argument is the socket, the second argument is the socket
address, and the third is the length of the socket address object.
m_socketAddress.sin_family = AF_INET;
m_socketAddress.sin_port = htons(m_port);
m_socketAddress.sin_addr.s_addr =
inet_addr(m_ip_address.c_str());
The code to bind the socket to the socket address is put in the
startServer method.
if (bind(m_socket,(sockaddr *)&m_socketAddress,
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 23 of 35
:
m_socketAddress_len) < 0)
{
exitWithError("Cannot connect socket to address");
return 1;
}
void TcpServer::startListen()
{
if (listen(m_socket, 20) < 0)
{
exitWithError("Socket listen failed");
}
std::ostringstream ss;
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 24 of 35
:
ss << "\n*** Listening on ADDRESS: "
<< inet_ntoa(m_socketAddress.sin_addr)
<< " PORT: " << ntohs(m_socketAddress.sin_port)
<< " ***\n\n";
log(ss.str());
}
Accepting connections
The accept() system call is used to process each connection thread
in the queue created by listen() . It does this by creating a new
socket with a connection thread, which can be used to receive and
send data between the client and the server.
int accept(
int sockfd,
struct sockaddr *restrict addr,
socklen_t *restrict addrlen
);
function since we no longer have any need for the server address
that was stored there previously. We will also be wrapping the code
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 25 of 35
:
for accepting connections in a method called acceptConnection()
system call is used to read the bytes onto the buffer. In the
Windows version, since read() is a POSIX I/O system call— which
isn’t directly available on Windows — the recv() function is used
instead.
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 26 of 35
:
);
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 27 of 35
:
}
Sending a response
The response data — which is a C-style string — is returned to the
client via the socket connection using the write() system call.
Similar to read() above, write() isn’t available on the Windows OS,
so send() is used in its stead.
The functions take in the socket object, the message data as well as
the size of the message data and write the data to the socket so the
client receives a response on their end of the connection.
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 28 of 35
:
//Windows version
int bytesSent;
long totalBytesSent = 0;
while (totalBytesSent < m_serverMessage.size())
{
bytesSent = send(m_new_socket, m_serverMessage.c_str(),
m_serverMessage.size(), 0);
if (bytesSent < 0)
{
break;
}
totalBytesSent += bytesSent;
}
if (totalBytesSent == m_serverMessage.size())
{
log("------ Server Response sent to client ------\n\n");
}
else
{
log("Error sending response to client.");
}
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 29 of 35
:
Conclusion
That is most of what you need to know to build a simple HTTP
server from scratch (for both Linux and Windows operating
systems). It is again noted that this project was done purely for
educational purposes. Ideally, you shouldn’t need to create a server
for use in your enterprise code. There’s a lot of things that go into
keeping your server secure that hasn’t been spoken about here
(server security is a topic as broad as they come haha). You will be
better of using an already implemented server in whatever
language you are programming with.
You can visit the GitHub repository (if you haven’t already done so)
to see the final state of the CPP files.
Responses (4)
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 30 of 35
:
Responses (4)
Write a response
Rud Merriam
Feb 17, 2022
The accept call does not create a new thread. One practice is to create a pool of threads to
handle the accept calls.
Good article.
4 1 reply Reply
2 Reply
Pm
Jul 4, 2022
You have to loop on the recv till you receive all the bytes in the message
Reply
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 31 of 35
:
More from Osamudiamen Azamegbe
Osamudiamen Azamegbe
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 32 of 35
:
Massimiliano Bastia ThreadSafe Diaries
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 33 of 35
:
Abhinav In Coding Beauty by Tari Ibaba
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 34 of 35
:
See more recommendations
https://osasazamegbe.medium.com/showing-building-an-http-server-from-scratch-in-c-2da7c0db6cb7 2025/07/22, 21 22
Page 35 of 35
: