This tutorial assumes that the reader has a basic knowledge of socket programming, i.e has a familiarity with basic server and client models. In the basic model, the server handles only one client at a time, which is a big assumption if one wants to develop any scalable server model. The simple way to handle multiple clients would be to spawn a new thread for every new client connected to the server.
Semaphores: Semaphore is simply a variable that is non-negative and shared between threads. This variable is used to solve the critical section problem and to achieve process synchronization in the multiprocessing environment.
sem_post: sem_post() increments (unlocks) the semaphore pointed to by sem. If the semaphore’s value consequently becomes greater than zero, then another process or thread blocked in a sem_wait(3) call will be woken up and proceed to lock the semaphore.
#include int sem_post(sem_t *sem);
sem_wait: sem_wait() decrements (locks) the semaphore pointed to by sem. If the semaphore’s value is greater than zero, then the decrement proceeds and the function returns, immediately. If the semaphore currently has the value zero, then the call blocks until either it becomes possible to perform the decrement (i.e., the semaphore value rises above zero), or a signal handler interrupts the call.
#include int sem_wait(sem_t *sem);
In this article, the Reader-Writers algorithm is implemented on the server-side.
Implementation: For the server-side, create two different threads; a reader thread, and a writer thread. First, declare a serverSocket, an integer, a variable to hold the return of socket function.
int serverSocket = socket(domain, type, protocol);
Then, after initializing all the necessary variables bind the socket.
bind: After the creation of the socket, the bind function binds the socket to the address and port number specified in addr(custom data structure). In the example code, we bind the server to the local host, hence INADDR_ANY is used to specify the IP address.
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen: It puts the server socket in a passive mode, where it waits for the client to approach the server to make a connection. The backlog defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED.
int listen(int sockfd, int backlog);
For more connection functions used in this article, please refer to this article for socket programming in C.
Approach:
Below is the implementation of the above approach:
Code for Server Side: