CE 155 - Socket Programming Example

### CE 155 - Socket Programming Example (Home)

For an introduction to sockets and network programming, visit the links below. These tutorials clearly explain the different system calls used for socket programming. There are two programs in this example --- the server and the client. The server accepts connections on port 1600. You may change this port by modifying the #defined value for SERVER_PORT. Once running, on another terminal, you can establish a connection to the server using telnet:

>     telnet [IP address] 1600

Replace [IP address] with the IP address of the computer the server program is running on. If you are running the server program on the same machine you are telnetting from, use localhost for [IP address].

After successfully establishing connection, typing /TIME will cause the server to return the current local time. Typing /QUIT will cause the server to break the connection.

The client program, on the other hand, demonstrates how to program sockets to connect to the server. The client will simply send /TIME and display the time returned by the server.

>     ./client [IP address]

To compile and link the programs:

>     gcc -o server server.c -Wall -Werror -lpthread
>     gcc -o client client.c -Wall -Werror

### server.c (download)

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <ctype.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <time.h>
    #include <pthread.h>
    #include <string.h>
    #include <signal.h>
    #include <stdlib.h>
    #define SUCCESS 0
    #define ERROR   1

    #define SERVER_PORT 1600
    #define MAX_MSG 256

    /* auxillary functions to read newline-terminated strings from a file/socket */
    int readnf (int, char *);
    int readline(int, char *, int);

    int server;         /* listening socket descriptor */

    /**
     * cleanup() is called to kill the thread upon SIGINT.
    **/
    void cleanup()
    {
        close(server);
        pthread_exit(NULL);
        return;
    } /* cleanup() */

    /**
     * Thread handler for incoming connections...
    **/
    void handler(void * paramsd) {
        struct sockaddr_in cliAddr;
        char line[MAX_MSG];
        char reply[MAX_MSG];
        int i;
        int client_local;   /* keep a local copy of the client's socket descriptor */
        int addr_len;       /* used to store length (size) of sockaddr_in */
        time_t currtime;
        char time_msg1[6] = {'/', 'T', 'I', 'M', 'E', '\n'};
        char time_msg2[6] = {'/', 'T', 'I', 'M', 'E', 13};
        char quit_msg1[6] = {'/', 'Q', 'U', 'I', 'T', '\n'};
        char quit_msg2[6] = {'/', 'Q', 'U', 'I', 'T', 13};
        char cmd[6];

        client_local = *((int *)paramsd); /* store client socket descriptor */
        addr_len = sizeof(cliAddr); /* store value of size of sockaddr_in */

        /* get clients name and store in cliAddr */
        getpeername(client_local, (struct sockaddr*)&cliAddr, &addr_len);
        /* reset line */
        memset(line, 0, MAX_MSG);

        /* now read lines from the client socket */
        while(readnf(client_local, line)!=ERROR)  /* loop - read from socket */
        {
            if (!strcmp(line,""))   /* string must not be null string */
                break;

            for (i = 0; i<6; i++)   /* get first 6 chars of string, capitalize */
                cmd[i] = toupper(line[i]);
            /* Did client ask for time? */
            if (strncmp(cmd, time_msg1, 6) == 0 || strncmp(cmd, time_msg2, 6) == 0) {
                printf("Received /TIME from %s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
                time(&currtime);                  /* get current time */
                strcpy(reply, ctime(&currtime));  /* copy into string */
                send(client_local, reply, strlen(reply), 0); /* return current time to client */
            }
            /* Does client want to quit? */
            else if (strncmp(cmd, quit_msg1, 6) == 0 || strncmp(cmd, quit_msg2, 6) == 0)
            {
                printf("Received /QUIT from %s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
                break;
            }
            else if (strlen(line) > 2)
            {
                strcpy(reply, "You:");
                strcat(reply, line);
                send(client_local, reply,strlen(reply),0);
            }
            /* reset line */
            memset(line,0,MAX_MSG);
        } /* while(readnf) */

        close(client_local);
        return;
    } /* handler() */

    /* main function */
    int main (int argc, char *argv[])
    {
        int client;         /* client socket descriptor */
        int addr_len;       /* used to store length (size) of sockaddr_in */
        pthread_t thread;   /* thread variable */

        struct sockaddr_in cliAddr;   /* socket address for client */
        struct sockaddr_in servAddr;  /* socket address for server */

        signal(SIGINT, cleanup);      /* now handle SIGTERM and SIGINT */
        signal(SIGTERM, cleanup);

        /* now create the server socket
           make it an IPV4 socket (PF_INET) and stream socket (TCP)
           and 0 to select default protocol type */
        server = socket(PF_INET, SOCK_STREAM, 0);
        if (server < 0) {
            perror("cannot open socket ");
            return ERROR;
        }

        /* now fill in values of the server sockaddr_in struct
           s_addr and sin_port are in Network Byte Order (Big Endian)
           Since Intel CPUs use Host Byte Order (Little Endian), conversion
           is necessary (e.g. htons(), and htonl() */
        servAddr.sin_family = AF_INET;  /* again ipv4 */
        servAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* local address */
        servAddr.sin_port = htons(SERVER_PORT);
        memset(servAddr.sin_zero, 0, 8);

        /* now bind server port
           associate socket (server) with IP address:port (servAddr) */
        if (bind(server, (struct sockaddr *) &servAddr, sizeof(struct sockaddr)) < 0) {
            perror("cannot bind port ");
            return ERROR;
        }

        /* wait for connection from client with a pending queue of size 5 */
        listen(server, 5);

        while(1) /* infinite loop */
        {
            printf("%s: waiting for data on port TCP %u\n", argv[0], SERVER_PORT);
            addr_len = sizeof(cliAddr);

            /* new socket for client connection
               accept() will block until a connection is present
               accept will return a NEW socket for the incoming connection
               server socket will continue listening
               store client address in cliAddr */
            client = accept(server, (struct sockaddr *) &cliAddr, &addr_len);
            if (client < 0) {
                perror("cannot accept connection ");
                break;
            }
            pthread_create(&thread, 0, (void*)&handler, (void*) &client);
        } /* while (1) */

        close(server);
        exit(0);
    } /* main() */

    /**
     * readnf() - reading from a file descriptor but a bit smarter
    **/
    int readnf (int fd, char *line)
    {
        if (readline(fd, line, MAX_MSG) < 0)
            return ERROR;
        return SUCCESS;
    }

    /**
     * readline() - read an entire line from a file descriptor until a newline.
     * functions returns the number of characters read but not including the
     * null character.
    **/
    int readline(int fd, char *str, int maxlen)
    {
      int n;           /* no. of chars */
      int readcount;   /* no. characters read */
      char c;

      for (n = 1; n < maxlen; n++) {
        /* read 1 character at a time */
        readcount = read(fd, &c, 1); /* store result in readcount */
        if (readcount == 1) /* 1 char read? */
        {
          *str = c;      /* copy character to buffer */
          str++;         /* increment buffer index */
          if (c == '\n') /* is it a newline character? */
             break;      /* then exit for loop */
        }
        else if (readcount == 0) /* no character read? */
        {
          if (n == 1)   /* no character read? */
            return (0); /* then return 0 */
          else
            break;      /* else simply exit loop */
        }
        else
          return (-1); /* error in read() */
      }
      *str=0;       /* null-terminate the buffer */
      return (n);   /* return number of characters read */
    } /* readline() */

### client.c (download)

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <unistd.h> /* close */
    #include <string.h>
    #include <stdlib.h>

    #define SERVER_PORT 1600
    #define MAX_MSG 256

    /* main() */
    int main (int argc, char *argv[]) {

        int client;  /* client socket */
        int rc;
        struct sockaddr_in local_addr, serv_addr;
        struct hostent * host;
        char message[6] = {'/','T','I','M','E','\n'};
        char date[25];

        if(argc < 2) {
            printf("usage: %s <server>\n",argv[0]);
            exit(-1);
        }

        /* get host address from specified server name */
        host = gethostbyname(argv[1]);

        if (host == NULL)
        {
            printf("%s: unknown host '%s'\n",argv[0],argv[1]);
            exit(-1);
        }

        /* now fill in sockaddr_in for remote address */
        serv_addr.sin_family = host->h_addrtype;
        /* get first address in host, copy to serv_addr */
        memcpy((char *) &serv_addr.sin_addr.s_addr, host->h_addr_list[0], host->h_length);
        serv_addr.sin_port = htons(SERVER_PORT);
        memset(serv_addr.sin_zero, 0, 8);

        /* create local stream socket */
        client = socket(PF_INET, SOCK_STREAM, 0);
        if (client < 0) {
            perror("cannot open socket ");
            exit(-1);
        }

        /* bind local socket to any port number */
        local_addr.sin_family = AF_INET;
        local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        local_addr.sin_port = htons(0);
        memset(local_addr.sin_zero, 0, 8);

        rc = bind(client, (struct sockaddr *) &local_addr, sizeof(local_addr));

        if (rc < 0)
        {
            printf("%s: cannot bind port TCP %u\n",argv[0],SERVER_PORT);
            perror("error ");
            exit(1);
        }

        /* connect to server */
        rc = connect(client, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
        if (rc < 0)
        {
            perror("cannot connect ");
            exit(1);
        }

        /* now send /TIME */
        rc = send(client, message, strlen(message) + 1, 0);

        if (rc < 0)
        {
            perror("cannot send data ");
            close(client);
            exit(-1);
        }

        /* we're expecting 25 chars from server, */
            read(client,date,25);

        printf(date);

        close(client);
        return 0;

    } /* main() */

### Links

- Beej's Guide to Network Programming
- An Introduction to Socket Programming
- UNIX Network Programming