client server model
Every network application is based on the client server model.
In brief:
- the client program sends a request to the server program
- the server program processes this requests
- the server sends back a response to the client program
- the client processes this request.
This is what happens each time you connect to the internet. This is the blueprint for all network communication.
the sockets interface
In Unix, and unix like systems, all I/O devices are modelled as files (sequence of bytes). The keyboard is a file, the headphone connected to your computer is a file, the monitor connected to it is a file. To write to an I/O device, you write to a file, and to read from an I/O device, you read from a file.
Network adapters are the same. When we want to send a request to a server, we write it to a file and when we get back a response we read a from a file.
This is what sockets are. They are functions provided to us by the operating system, aka system calls, we use to create and configure the files used to write to and read from I/O devices.
In addition from creating the files, through the socket interface, the kernel also wraps our message in the protocol of our choice, here TCP/IP, so that we can correctly address our client or server.
Depending on if we are creating a server or a client, the system calls we use are different. Here’s an overview of the function calls needed for a client and a server to communicate:
client side:
First things first, we need to retrieve the server’s address. We can do this in various ways. We can either use the getaddrinfo
function to perform a DNS lookup for a specific node (hostname) and service (http/ssh or the port number). Or we can use the gethostbyname
function to get the server’s address via the hostname.
We then create the socket. As mentioned the socket is just a file, and like creating any file the kernel returns it corresponding descriptor.
- the
domain
is used to specify the protocol (we will use ) - the
type
indicates the type of communication (streams/datagrams or raw) - the
protocol
idk what this does. - it returns a file descriptor.
The socket function creates the file but it is not yet connected to anything.
That is where the connect
function comes. Using the socket we created above and the server’s address we obtained, we attempt to send a connection request to the server.
socket
is the return value of the socket functionaddress
is the server address obtained through thegetaddrinfo
orgethostbyname
functionsaddress_len
is the length ofaddress
Once we have done the steps above we can send and receive messages through the send
and recv
system calls.
socket
: the socket descriptor created by the socket functionbuffer
: the message we want to sendlength
: length of bufferflags
: not used
socket
: the socket descriptor created by the socket functionbuffer
: the message we want to sendlength
: length of bufferflags
: not used
When we are done we can close the socket using close
.
Here’s a code snippet for creating a client and connecting to a server:
Server side
Now let’s hop on the server side. We create the server socket the same way we created the client’s socket. Depending on our use case we may or may not need to use the getaddrinfo
function. If we accept all connections associated with this host then we don’t use it, if we only accept connections addressed to specific IP addresses then we can use it to look up what the IP addresses associated to this host is.
After we create the socket descriptor, we need to bind it to the server’s address. We do so by calling the bind
function:
socket
: the server’s socket descriptoraddress
: the server’s addressaddress_len
: the size of address
Then we pass the socket descriptor into the listen
function to convert it from an active socket to a listening socket.
sockfd
: the server’s socket descriptorbacklog
: the number of connections to queue up before refusing connections
Here’s a code snippet putting all this together:
Once we receive a connection request we can use the same send
and receive
functions to communicate with the client.
http web server
HTTP is an application protocol used to send HTML files. It is an application layer protocol meaning it dictates the behavior of the data we send between client and server.
Once we have established a server that is capable of accepting connections, we need to make sure that it is a able to process and send back data that complies with the http protocol. The web server can either serve static content, a page that is prebuilt during development, or dynamic content, a page that is built on a per request basis, by executing some code on the server.
http request
http request have a request line followed by zero or more request header.
the request line has the following fields:
<method> <\uri> <\version>
- method: this indicates the operation that the client wants us to perform, can be GET, POST, OPTIONS, HEAD, PUT, DELETE, and TRACE. For our implementation we will only handle GET requests.
- uri: this the Uniform Resource Identifier, it indicates which file/resource the client wants us to process.
- version: indicates the http version.
the request header has the following fields: <\header name>: <\header data>
http response
http responses have a response line followed by zero or more response headers followed by zero or more response bodies.
response have the following fields: <\version> <\status code> <\status message>
a full list of status codes and status messages can be found here.
the response body is where we have the html file that is displayed on our browser.
static content
To display static content, all we need to do is copy the file specified by the uri into the body of the http response.
dynamic content
To generate dynamic content, the server creates a child process to execute some code and waits for it to finish. The common gateway interface standard defines how the server process passes information to child process and where the child process sends its outputs.