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;
}
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;
}
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;
}
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
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);
}
I register this handler with function:
signal(SIGINT,signal_handler);
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;
}
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);
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));
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));
}
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);
Step 3: Compile and run
I compile program with simple command
gcc -o main mainc
And run it with command
./main
I test my server with command
curl -X GET 127.0.0.1:8080
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 (2)
Nice!! I've really been wanting to make a project like this. I'm definitely going to follow along.
Very good. When will you upload the part 2. In this part only socket programing included.