Skip to main content

Concurrent Time Server application using UDP

Below is a simple implementation of a Concurrent Time Server application using UDP in C. This program consists of two parts: the server and the client.

Server Code

The server listens for time requests and sends its system time back to the client.

// Server Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 12345
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in server_addr, client_addr;
    char buffer[BUFFER_SIZE];
    socklen_t addr_len = sizeof(client_addr);

    // Create socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    memset(&client_addr, 0, sizeof(client_addr));

    // Configure server address
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // Bind the socket to the address
    if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Server is running and waiting for time requests on port %d...\n", PORT);

    while (1) {
        memset(buffer, 0, BUFFER_SIZE);

        // Receive message from client
        int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &addr_len);
        if (n < 0) {
            perror("Receive failed");
            continue;
        }

        printf("Received request from client: %s\n", inet_ntoa(client_addr.sin_addr));

        // Get the current time
        time_t current_time = time(NULL);
        char *time_str = ctime(&current_time);

        // Send the current time back to the client
        sendto(sockfd, time_str, strlen(time_str), 0, (struct sockaddr *)&client_addr, addr_len);
        printf("Sent time to client: %s", time_str);
    }

    close(sockfd);
    return 0;
}

Client Code

The client sends a request to the server and displays the received time.

// Client Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 12345
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];
    char *request_message = "TIME_REQUEST";

    // Create socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&server_addr, 0, sizeof(server_addr));

    // Configure server address
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Replace with server IP if needed

    // Send time request to server
    if (sendto(sockfd, request_message, strlen(request_message), 0, 
               (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Send failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Time request sent to server.\n");

    // Receive response from server
    int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, NULL);
    if (n < 0) {
        perror("Receive failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    buffer[n] = '\0'; // Null-terminate the received string
    printf("Current time from server: %s\n", buffer);

    close(sockfd);
    return 0;
}

How to Run

  1. Compile the server and client programs separately:
    gcc -o server server.c
    gcc -o client client.c
  2. Run the server program on the remote server:
    ./server
  3. Run the client program on the same or another machine:
    ./client

Expected Output

  • Server:

    Server is running and waiting for time requests on port 12345...
    Received request from client: 127.0.0.1 Sent time to client: Thu Jan 10 14:25:30 2025
  • Client:

    Time request sent to server.
    Current time from server: Thu Jan 10 14:25:30 2025

Server Code Breakdown

Imports and Definitions

#include <stdio.h> // Standard I/O operations
#include <stdlib.h> // Standard library functions (exit, malloc, etc.) #include <string.h> // String manipulation functions #include <time.h> // For fetching system time #include <unistd.h> // For close() function #include <arpa/inet.h> // For socket programming (struct sockaddr_in, etc.)
  • Purpose: Includes the necessary libraries for socket communication, time manipulation, and basic system operations.

Constants

#define PORT 12345 // The server listens on this port
#define BUFFER_SIZE 1024 // Buffer size for communication
  • PORT: The port number on which the server will listen for requests.
  • BUFFER_SIZE: Defines the maximum size of data the server can send/receive.

Socket Creation

int sockfd;
struct sockaddr_in server_addr, client_addr; socklen_t addr_len = sizeof(client_addr); char buffer[BUFFER_SIZE]; // Create socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("Socket creation failed"); exit(EXIT_FAILURE); }
  • socket(AF_INET, SOCK_DGRAM, 0): Creates a UDP socket.
  • Error Handling: If socket creation fails, the program exits.

Server Address Configuration

memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // IPv4 server_addr.sin_addr.s_addr = INADDR_ANY; // Accept connections from any address server_addr.sin_port = htons(PORT); // Convert port to network byte order
  • INADDR_ANY: Allows the server to listen on all available network interfaces.
  • htons(PORT): Converts the port number to network byte order.

Binding the Socket

if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed"); close(sockfd); exit(EXIT_FAILURE); }
  • bind: Associates the socket with the specified address and port.
  • Error Handling: Exits if the binding fails.

Listening for Requests

while (1) {
memset(buffer, 0, BUFFER_SIZE); // Receive message from client int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &addr_len); if (n < 0) { perror("Receive failed"); continue; } printf("Received request from client: %s\n", inet_ntoa(client_addr.sin_addr));
  • recvfrom: Waits for a message from a client.
  • inet_ntoa(client_addr.sin_addr): Converts the client’s IP address to a human-readable format.

Send System Time to Client

// Get the current time
time_t current_time = time(NULL); char *time_str = ctime(&current_time); // Send the current time back to the client sendto(sockfd, time_str, strlen(time_str), 0, (struct sockaddr *)&client_addr, addr_len); printf("Sent time to client: %s", time_str); }
  • time(NULL): Fetches the current system time.
  • ctime: Converts the time to a human-readable string.
  • sendto: Sends the time string to the client.

Closing Resources

close(sockfd);
  • Ensures the socket is closed when the server is terminated.

Client Code Breakdown

Imports and Definitions

#include <stdio.h>
#include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define PORT 12345 #define BUFFER_SIZE 1024
  • Includes libraries similar to the server code.
  • Defines constants for port number and buffer size.

Socket Creation

int sockfd;
struct sockaddr_in server_addr; char buffer[BUFFER_SIZE]; char *request_message = "TIME_REQUEST"; // Create socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("Socket creation failed"); exit(EXIT_FAILURE); }
  • socket(AF_INET, SOCK_DGRAM, 0): Creates a UDP socket.
  • Error Handling: Exits if socket creation fails.

Server Address Configuration

memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // IPv4 server_addr.sin_port = htons(PORT); // Convert port to network byte order server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Replace with server's IP if remote
  • inet_addr("127.0.0.1"): Converts the server's IP address to binary format.

Send Time Request

if (sendto(sockfd, request_message, strlen(request_message), 0,
(const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("Send failed"); close(sockfd); exit(EXIT_FAILURE); } printf("Time request sent to server.\n");
  • sendto: Sends a message to the server requesting the time.

Receive Server Response

int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, NULL);
if (n < 0) { perror("Receive failed"); close(sockfd); exit(EXIT_FAILURE); } buffer[n] = '\0'; // Null-terminate the received string printf("Current time from server: %s\n", buffer);
  • recvfrom: Waits for a response from the server.
  • Null-Termination: Ensures the received string is properly terminated before printing.

Close the Socket

close(sockfd);
  • Ensures the socket is closed when the program ends.

Comments

Popular posts from this blog

Server/Client Communication-python

The basic mechanisms of client-server setup are: A client app send a request to a server app.  The server app returns a reply.  Some of the basic data communications between client and server are: File transfer - sends name and gets a file.  Web page - sends url and gets a page.  Echo - sends a message and gets it back.  Client server communication uses socket.              To connect to another machine, we need a socket connection. What's a connection?  A relationship between two machines, where two pieces of software know about each other. Those two pieces of software know how to communicate with each other. In other words, they know how to send bits to each other. A socket connection means the two machines have information about each other, including network location (IP address) and TCP port. (If we can use anology, IP address is the phone number and the TCP port is the extension).  A so...

Banker's Algorithm

Banker's algorithm is a deadlock avoidance algorithm. It is named so because this algorithm is used in banking systems to determine whether a loan can be granted or not. Consider there are n account holders in a bank and the sum of the money in all of their accounts is S. Everytime a loan has to be granted by the bank, it subtracts the loan amount from the total money the bank has. Then it checks if that difference is greater than S. It is done because, only then, the bank would have enough money even if all the n account holders draw all their money at once. Banker's algorithm works in a similar way in computers. Whenever a new process is created, it must exactly specify the maximum instances of each resource type that it needs. Let us assume that there are n processes and m resource types. Some data structures are used to implement the banker's algorithm. They are: Available: It is an array of length m . It represents the number of available resourc...

Inter Process Communication-Message Queue

Interprocess communication (IPC) is a set of programming interfaces that allow a programmer to coordinate activities among different program processes that can run concurrently in an operating system. This allows a program to handle many user requests at the same time. Since even a single user request may result in multiple processes running in the operating system on the user's behalf, the processes need to communicate with each other. The IPC interfaces make this possible. Each IPC method has its own advantages and limitations so it is not unusual for a single program to use all of the IPC methods . Message Based Communication Messages are a very general form of communication. Messages can be used to send and receive formatted data streams between arbitrary processes. Messages may have types. This helps in message interpretation. The type may specify appropriate permissions for processes. Usually at the receiver end, messages are put in a queue. Messages may also be fo...