Fundamental C - Socket Server
Written by Harry Fairhead   
Monday, 08 August 2022
Article Index
Fundamental C - Socket Server
HTML
WinSock Server

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.

  1. About C
      Extract Dependent v Independent
                  & Undefined Behavio
  2. Getting Started With C Using NetBeans
  3. Control Structures and Data
  4. Variables
      Extract Variables 
  5. Arithmetic  and Representation
      Extract Arithmetic and Representation 
  6. Operators and Expression
      Extract: Expressions
      Extract Side Effects, Sequence Points And Lazy Evaluation
      First Draft of Chapter: Low Down Data
  7. Functions Scope and Lifetime
  8. Arrays
      Extract  Simple Arrays 
      Extract  Ennumerations 
  9. Strings
      Extract  Simple Strings
     
    Extract: String I/O ***NEW!!
  10. Pointers
      Extract  Starting Pointers
      Extract  Pointers, Cast & Type Punning
  11. Structs
      Extract Basic Structs 
      Extract Typedef
  12. Bit Manipulation
      Extract Basic Bits
      Extract Shifts And Rotates 
  13. Files
     Extract Files
     
    Extract Random Access Files 
  14. Compiling C – Preprocessor, Compiler, Linker
     Extract Compilation & Preprocessor

Also see the companion volume: Applying C

<ASIN:1871962609>

<ASIN:1871962463>

<ASIN:1871962617>

<ASIN:1871962455>

 

 

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.

  • Socket Basics
  • Socket Functions
  • Create a socket
  • Connect a socket to an address
  • Bind a socket to an address
  • Reading and Writing
  • Listen and Accept
  • A Web Client
  • A WinSock Web Client
  • Connecting Using a URL

A Server

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);
listen(sockfd, 10);

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;
socklen_t addr_size = sizeof client_addr;
int client_fd = accept(sockfd, 
                   (struct sockaddr *) &client_addr,
                    &addr_size);

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[2048];
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
       Content-type: text/html\r\n\r\n";

which corresponds to sending:

HTTP/1.0 200 OK
Server: C
Content-type: text/html 

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 )