Simple time-of-day TCP Client-Server model example using sockets in C

By Nischal Lal Shrestha | On Wed, Oct 14 2020 | Filed under Code

Image for Simple time-of-day TCP Client-Server model example using sockets in C

This program returns time of day whenever a client send server a packet. Using this example we will understand basics of TCP sockets.

In a client-server model, a server is an application program that offers a service over a network. The server's code runs first, which opens a port and accepts an incoming request. When a request arrives it forms a response and returns the result to the requester. A program becomes a client when it sends a request to a server and waits for a response.


Time-of-Day

Here, when a client is connected to a server. The server will send current time information to the client and the client will print the information in the screen.

Server Side Code

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>

#define MAXLINE 1024
#define LISTENQ 10

typedef struct sockaddr SA;

int main(int argc, char **argv){
    int listenfd, connfd;
    struct sockaddr_in servaddr, cliaddr;
    char buff [MAXLINE];
    time_t ticks;
    int port;
    socklen_t len;

    listenfd = socket(AF_INET, SOCK_STREAM, 0); // Creates a TCP Socket

    port = atoi(argv[1]);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    printf("Server is waiting connection at port %d\t\n", port);
    listen(listenfd, LISTENQ);

    for ( ; ; ) {
        len = sizeof(cliaddr);
        connfd = accept(listenfd, (SA *) &cliaddr, &len);
        printf("Connection from %s, port %d\n", inet_ntop(AF_INET,
        &cliaddr.sin_addr.s_addr, buff, sizeof(buff)), ntohs(cliaddr.sin_port));
        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        write(connfd, buff, strlen(buff));
        close(connfd);
    }

}

1. Include header files

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>

These line includes numerous system headers that are needed by most network programs

2. Defines Constants

#define MAXLINE 1024
#define LISTENQ 10

Defines various constants that we use(e.g., MAXLINE, LISTENQ)

3. Gives type "struct sockaddr" a new name to shorten the code.

typedef struct sockaddr SA;
  1. Starts main function and it accepts command line arguments.
int main(int argc, char **argv){

argc (argument count) and argv (argument vector) are how command line arguments are passed to main() in C and C++.

argc will be the number of strings pointed to by argv.

5. Creates a TCP Socket.

listenfd = socket(AF_INET, SOCK_STREAM, 0); // Creates a TCP Socket

The socket function creates a socket on demand. The function takes three integer arguments and returns an integer descriptor which we can use to identify the socket in all future function calls (e.g., the calls to connect and read that follow).

6.Initialize the server address by the port and IP.

port = atoi(argv[1]);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);

7. Bind the socket descriptor to the server address.

bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

Once a socket has been created, a server uses the bind function to establish a local address for the socket†. Bind has the following form.

bind(descriptor, localaddr, addrlen)

8. Turn on the socket to listen for incoming connections.

listen(listenfd, LISTENQ);

Socket function listen allows servers to prepare a socket for incoming connections. In terms of the underlying protocols, listen puts the socket in a passive mode ready to accept connections. Only servers use listen. In addition to placing a protocol in passive mode, listen contains an argument that configures the size of a queue for incoming re- quests. A call has the form:

listen(descriptor, qlength);

Argument descriptor gives the descriptor of a socket that should be prepared for use by a server, and argument qlength specifies the length of the request queue for that socket.

9. Infinite loop

for ( ; ; ) {
. . .
}

10. Store the client’s address and socket descriptor by accepting an incoming connection.

len = sizeof(cliaddr);
connfd = accept(listenfd, (SA *) &cliaddr, &len);

Once a socket has been established, the server needs to wait for a connection. To do so, the server calls function accept. A call to accept blocks until a new connection request arrives. The call has the form:

newsock = accept(descriptor, addr, addrlen)

11. Sending data through a socket.

ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
write(connfd, buff, strlen(buff));

This calculate current time and send it to client through socket.

12. Terminate the connection.

close(connfd);

Client Side Code

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>

#define MAXLINE 1024
#define LISTENQ 10

typedef struct sockaddr SA;

int main (int argc, char **argv){
    int sockfd, n;
    char recvline [MAXLINE + 1];
    struct sockaddr_in servaddr;
    int port;

    if(argc != 3){
        printf("Usage: a.out <IPaddress> <port no.>");
    }
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ // Creates a TCP Socket
            printf("Socket Error");
    }

    port = atoi(argv[2]); // ascii to integer conversion

    bzero(&servaddr, sizeof(servaddr)); // fills servaddr with zeros.

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);

    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
    // pton = presentatin to network
        printf("inet_pton error for %s", argv[1]);
    }

    if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0){
     // Establishes a TCP Connnection
        printf("Connect Error.\n");
    }

    while ((n=read(sockfd, recvline, MAXLINE)) > 0){
    // reads server reply.
        recvline[n] =0;

        if (fputs(recvline, stdout) == EOF){
            printf("fputs error.\n");
        }
    }

    if (n <0){
        printf("read error");
    }



}

Instructions for Lab 1

  1. Create two files for the Client and server program named as daytimetcpserver.c and daytimetcpclient.c
  2. Write a server and client program
  3. Save both files in the same directory
  4. Compile both programs using GCC compiler.

example: gcc -o server daytimetcpserver.c (It creates an output file server)

do similar for client program

  1. Open terminal and run server program

example: ./server <portno> (Specify port number)

6: In another terminal run client program

example: ./client <server ip> <port no> (Specify server ip and port no)

  1. Observer an output.
  2. Submit your code