|Fundamental C - Socket Server|
|Written by Harry Fairhead|
|Monday, 08 August 2022|
Page 1 of 3
This extract, from my book on programming C in an IoT context explains how to use sockets to create a simple web server. Servers are more difficult because they require asychronous programming.
Fundamental C: Getting Closer To The Machine
Now available as a paperback and ebook from Amazon.
Also see the companion volume: Applying C
Earlier in the chapter we looked at the basic use of sockect and how to use them to create an HTML web page client. In this extract the subject is extended to using sockets to implement a server - slighly more difficult because of the need to use asychronous programming.
In Chapter but not in this extract.
A server is more or less the same as a client from an implementation point of view. The only real difference is that it has to wait until a client connects before dealing with a transaction.
The first step is to create the socket and this follows the same pattern as for the client. We could simply set up the address structures and create a socket, but now we know how to use getaddrinfo it is easier to use this to do the job automatically and flexibly:
struct addrinfo hints, *server; memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; getaddrinfo(NULL, "1024", &hints, &server);
The AI_PASSIVE flag assigns the current system's IP address. You can easily get address structures for alternative addresses such as IPv6 using this, but for simplicity we just ask for an IPv4 address. Notice the specification of port 1024, which isn’t the usual HTTP port. The reason for using it is that ports below 1024 are restricted and programs need to be run as root to use them. If you want to use 80 for an HTTP server socket you have to compile the program and run it using sudo, or however you give a program root access in the operating system you are using. After the call to getaddrinfo, the structs we need to create sockets are ready to be used:
int sockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol); bind(sockfd, server->ai_addr, server->ai_addrlen);
You can see how easy getaddrinfo makes everything. The call to bind assigns the socket the IP address of the machine on port 1024 and listen starts things going with a queue of ten pending clients.
We can now use accept to wait for a client to connect:
struct sockaddr_storage client_addr;
At this point our program is blocked waiting for a client to connect to the socket. If you want to keep processing things then you need to use a socket in non-blocking mode, see later.
For the moment we can assume that when accept returns there is a new socket descriptor in client and details of the client in client_addr. Again for simplicity, we are not going to check to see who the client is, just serve them a web page. The client will first send the server an HTTP GET packet, assuming they do want to GET a web page. We can read this in using:
char buffer; int n = read(client_fd, buffer, 2048); printf("%s", buffer);
The data in the GET headers tell the server which file is required and you can do some string handling to process it to get the name. In this case we are going to send the same HTML file no matter what the client asked for. To do this we need some HTTP headers defining what we are sending back and some HTML to define the page we are sending. The simplest set of headers that work is:
char headers = "HTTP/1.0 200 OK\r\n Server: C\r\n
which corresponds to sending:
HTTP/1.0 200 OK
with a blank line to mark the end of the headers.
Notice that we have swapped to HTTP 1.0 because this is simpler and works with a smaller set of headers. If you want to support HTTP 1.1 then you need to specify the Content-Length header and the Connection header.
|Last Updated ( Saturday, 13 August 2022 )|