DEV Community

loading...

[Challenge] Write a full web application in C - part 1, make the server wake up

pmtoan profile image Minh Toan ・4 min read

Hello everyone, today I have started a challenge for myself. I must build a web application in the C programming language. In this challenge, I will build an online book store website and I use C for backend service. I hope you interested in this challenge.

This post is just part 1 of this challenge. In this post, I will write a simple TCP/IP socket server on the Unix-liked system (Linux, macOS...). Okay, let's go.

Step 1: Create soc.h file

Write socket creation function
In this function, I create a socket, bind socket to a port number and make it listen.

int open_tcp_soc(int listen_port) {
    int sd;
    sd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(listen_port);
    if ((bind(sd, (struct sockaddr*)&server_addr,sizeof(server_addr))) < 0)
        return -1;
    if (listen(sd, 10) != 0)
        return -1;
    return sd;
}
Enter fullscreen mode Exit fullscreen mode

Write a function read data from socket

int receive_message(int socket, char* buf, int len)
{
    char *s = buf;
    int slen = len;
    int c = recv(socket, s, slen, 0);
    if (c < 0)
        return c;
    else if (c == 0)
        buf[0] = '\0';
    else
        s[c]='\0';
    return len - slen;
}
Enter fullscreen mode Exit fullscreen mode

Write a function send data to socket

int send_message(int socket, const char* msg)
{
    int result = send(socket, msg, strlen(msg), 0);
    return result;
}
Enter fullscreen mode Exit fullscreen mode

I put three above functions into file soc.h. This file include some others header files:

#ifndef __SOC_H__
#define __SOC_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

// functions here

#endif
Enter fullscreen mode Exit fullscreen mode

Step 2: Build main.c file

Write signal handler function
This function handles a SIGTERM, SIGINT signal send into the main application. This makes my program exits when I press Ctrl+C.

void signal_handler(int sig)
{
    printf("\nserver exited\n");
    close(server);
    exit(0);
}
Enter fullscreen mode Exit fullscreen mode

I register this handler with function:

signal(SIGINT,signal_handler);
Enter fullscreen mode Exit fullscreen mode

Write a request handler function
This function response a simple message: "Hello World" when client request to my server.

char* handle_request(const char* msg) {
    char* response_text = (char*)malloc(REQUEST_SIZE);
    sprintf(response_text, "HTTP/1.1 200 OK\n\nHello World\n");
    return response_text;
}
Enter fullscreen mode Exit fullscreen mode

Make server listen
Juts call function I created in step 1

#define LISTEN_PORT 8080

int server;
printf("server is listening on port %d\n", LISTEN_PORT);
server = open_tcp_soc(LISTEN_PORT);
Enter fullscreen mode Exit fullscreen mode

Receive client request and send a response to client

struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
int connect;
char* message = (char*)malloc(REQUEST_SIZE);
connect = accept(server,(struct sockaddr *)&client_addr,&address_size);
struct sockaddr_in *coming = (struct sockaddr_in *)&client_addr;
unsigned char *ip = (unsigned char *)&coming->sin_addr.s_addr;
unsigned short port = coming->sin_port;
if (connect < 0)
{   
    // can't accept connect from client
    printf("can not connect with %d.%d.%d.%d:%d\n", ip[0], ip[1], ip[2], ip[3], port);
    continue;
}
printf("connected with %d.%d.%d.%d:%d\n", ip[0], ip[1], ip[2], ip[3], port);

receive_message(connect, message, REQUEST_SIZE);
send_message(connect, handle_request(message));
Enter fullscreen mode Exit fullscreen mode

Make server run forever
I put my server handler step into a forever loop. This because my server needs to listen to requests and send responses over and over again.

while(1)
{
    connect = accept(server,(struct sockaddr *)&client_addr,&address_size);
    struct sockaddr_in *coming = (struct sockaddr_in *)&client_addr;
    unsigned char *ip = (unsigned char *)&coming->sin_addr.s_addr;
    unsigned short port = coming->sin_port;
    if (connect < 0)
    {   
        // can't accept connect from client
        printf("can not connect with %d.%d.%d.%d:%d\n", ip[0], ip[1], ip[2], ip[3], port);
        continue;
    }
    printf("connected with %d.%d.%d.%d:%d\n", ip[0], ip[1], ip[2], ip[3], port);

    receive_message(connect, message, REQUEST_SIZE);
    send_message(connect, handle_request(message));
}
Enter fullscreen mode Exit fullscreen mode

Make my server can handle multiple requests at the same time
You can see that my server can handle only one request at the same time. If my handle_request function takes too long to execute, other requests will be waiting for the current request. So, I can handle this by using the fork system call. You can read more about the fork system call here: http://man7.org/linux/man-pages/man2/fork.2.html
After my server connected with the client, my server will fork a child process to handler request and sen response to the client.

pid_first = fork();
if (pid_first == 0)
{
    // clear signal handler in the child process
    signal(SIGINT, SIG_DFL);
    pid_t pid_first;
    pid_t pid_second;
    pid_second = fork();
    if (pid_second == 0)
    {
        signal(SIGINT, SIG_DFL);
        receive_message(connect, message, REQUEST_SIZE);
        send_message(connect, handle_request(message));
        close(connect);
    }
    close(connect);
    exit(0);
}
wait(NULL);
close(connect);
Enter fullscreen mode Exit fullscreen mode

Step 3: Compile and run

I compile program with simple command

gcc -o main mainc
Enter fullscreen mode Exit fullscreen mode

And run it with command

./main
Enter fullscreen mode Exit fullscreen mode

I test my server with command

curl -X GET 127.0.0.1:8080
Enter fullscreen mode Exit fullscreen mode

I can get a 'Hello World' message from server.

You can see the project code on my Github Repo:

This is part 1 of my challenge. I hope you have a good time and wait for the next part :D

Discussion (1)

Collapse
ryanolsonx profile image
Ryan Olson

Nice!! I've really been wanting to make a project like this. I'm definitely going to follow along.

Forem Open with the Forem app