Recent Changes - Search:

Network Programming

This website demonstrates using wikis as teaching and learning tool.

The course instructor is happy to share the teaching materials here with those who find it readable.

Network Programming in C and UNIX

A Network Programming Lecture by Steven Choy

Lecture Overview: This lecture explains the concept of socket programming, and outlines the structure of a socket address and some system calls for socket programming. It also shows some example programs that implements the socket programming using C programming language for TCP and UDP communication.

Reference: Beej's Guide to Network Programming


Review of Basic Concepts on Network Programming

  • What is a socket?
  • Two Types of Internet Sockets
One is "Stream Sockets"; the other is "Datagram Sockets"
Datagram sockets are sometimes called "connectionless sockets".
Stream sockets are reliable two-way connected communication streams.
  • IP Addresses, versions 4 and 6
  • Port Numbers

Socket Structure

  • The structure of a socket address is used to organize the information for the communication of application programs. What you need to do is to fill in all of the information and data in a socket, and then the lower layers will complete the data communication.
  • The definition of the structure of a general socket address can be found in <sys/socket.h>:
        struct sockaddr {
            u_short sa_family; /* address family: AF_xxx value */
            char sa_data[14]; /* up to 14 bytes of protocol */
            /* specific address */
        }
  • In the Internet family, we use the following specific structures for defining IP socket address in <netinet/in.h>:
        struct in_addr {
            u_long s_addr; /* 32-bit IP address */
            /* network byte ordered */
        }

        struct sockaddr_in {
            short sin_family; /* AF_INET */
            u_short sin_port; /* 16-bit TCP or UDP port number */
            struct in_addr sin_addr; /* 32-bit IP address */
            /* network byte ordered */
            char sin_zero[8]; /* unused */
        }

        struct sockaddr_in {
            short int          sin_family;  // Address family, AF_INET
            unsigned short int sin_port;    // Port number
            struct in_addr     sin_addr;    // Internet address
            unsigned char      sin_zero[8]; // To make it the same size as struct sockaddr
        };

Socket System Calls

Socket

  • Before starting the communication, we need the socket system call to specify the type of communication protocol desired (e.g. TCP, UDP, etc.) and to define the end point.
      #include <sys/types.h>
      #include <sys/socket.h>
      int socket(int family, int type, int protocol);
  • The following constants are the choices to put into the first argument family to indicate which protocol is used:
      AF_UNIX or AF_LOCAL (UNIX internal protocols);
      AF_INET (Internet protocols, IPv4);
      AF_INET6 (IPv6 protocols);
      AF_NS (Xerox NS protocols); and
      AF_IMPLINK (IMP link layer).
  • We use AF_INET addresses in the later examples, because they are the most useful and widely used address family.
  • For the second argument type, the choices are shown below.
  • The socket system call returns an integer called a socket descriptor, or sockfd. This integer represents a communication end point whose communication protocol is selected after executing the socket system call.
  • On successful completion, the socket system call will return a nonnegative integer for the socket descriptor. Otherwise, it will return –1 to indicate that an error occurred.

bind

  • The bind system call is used to assign an address (both an IP address and a port number) to a socket.
        #include <sys/types.h>
        #include <sys/socket.h>
        int bind(int sockfd, struct sockaddr *myaddr,
        int addrlen);
  • The first argument sockfd is the socket descriptor returned by the socket system call.
  • The second argument *myaddr is a pointer to the socket address myaddr
  • The third one addrlen is the length of the socket address myaddr.
  • The output returns 0 if the bind system call is successfully executed; otherwise, it returns -1 if it failed.

connect

  • The connect system call allows a client to establish a connection with a server.
        #include <sys/types.h>
        #include <sys/socket.h>
        int connect(int sockfd, struct sockaddr *servaddr, int addrlen);
  • The first argument sockfd is the socket descriptor returned by the socket system call.
  • The second argument *servaddr is a pointer to the socket address servaddr, which is the socket address of the server.
  • The third one addrlen is the length of the socket address servaddr.
  • The client uses TCP communication system call to establish a connection to its corresponding server.
  • If the system call is successfully executed, the connection will be established and then data or packets can be exchanged through the connection; otherwise, an error will be returned to the process.
  • Note that we do not use this system call in UDP communication.
  • Note that both system connect and bind calls require the pointer to the socket address structure (*myaddr and *servaddr) only, not the protocol.
  • The reason is that the first 2 bytes of the socket address store the address family AF_INET, and the sockfd argument yields the desired protocol after the socket system call has created the socket descriptor.

listen

  • A TCP server uses the listen system call to indicate the number of connections that it is willing to receive.
  • It is used after executing the socket and bind system calls and before executing the accept system call. Note that only TCP communication requires this system call, whereas UDP communication does not.
        #include <sys/types.h>
        #include <sys/socket.h>
        int listen(int sockfd, int backlog);
  • The first argument sockfd is the socket descriptor returned by the socket system call.
  • The second argument backlog specifies the maximum number of connections allowed to be queued by the system while it waits for the server to execute the accept system call.

accept

  • The accept system call takes the first connection request from the queue that stores all incoming connection requests, and creates another socket with the same properties as sockfd.
        #include <sys/types.h>
        #include <sys/socket.h>
        int accept(int sockfd, struct sockaddr *peer, int *addrlen);
  • The first argument sockfd is the socket descriptor of the first connection request from the waiting queue (if any).
  • The second and third arguments peer and addrlen are used to return the address of the connected peer process (the client), and thus they are pointers but not variables. The second argument peer is the socket address of the client process, and the third argument addrlen is the size of this address.

send, sendto, recv and recvfrom

  • These four system calls are used for message exchange. The send and recv system calls are for TCP communication, whereas the sendto and recvfrom system calls are for UDP communication.
        #include <sys/types.h>
        #include <sys/socket.h>

        int send(int sockfd, char *buff, int nbytes, int flags);
        int recv(int sockfd, char *buff, int nbytes, int flags);

        int sendto(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *to, int addrlen);
        int recvfrom(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *from, int *addrlen);

close

  • The close system call is used to close one end of a socket connection.
        int close(int fd);
  • The following is a summary of the socket system calls.
  • The following shows the general client and server operations for connection-oriented protocol.

Socket programming flow

  • The following two diagrams illustrate the general program flow of two communication protocols: TCP and UDP.
TCP Program Flow (Source: Stevens 1994)
UDP Program Flow (Source: Stevens 1994)

Socket programming Examples

  • Example 1: Daytime Server
/*****************************************/
/********** A simple day time server *****/
/*****************************************/

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

#define SERV_TCP_PORT 50000

#define MAXLINE 4096
#define LISTENQ 1024 /* 2nd argument to listen() */

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

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_TCP_PORT);    /* daytime server */

    bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    listen(listenfd, LISTENQ);

    for ( ; ; ) {
        connfd = accept(listenfd, (struct sockaddr *) NULL, NULL);

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

        close(connfd);
    }
}
 
  • Example 2: Daytime Client
/*****************************************/
/******* A simple daytime client *********/
/*****************************************/

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

#define SERV_TCP_PORT 50000

char *SERV_HOST_ADDR = NULL;

#define MAXLINE 4096

int
main(int argc, char **argv)
{
     int     sockfd, n;
     char    recvline[MAXLINE+1];
     struct  sockaddr_in servaddr;
     struct hostent *hp;  
     char **p;  
     struct in_addr in;  

     hp = gethostbyname(argv[1]);
     if (hp != NULL) {
         p = hp->h_addr_list;
         memcpy(&in.s_addr, *p, sizeof(in.s_addr));
         SERV_HOST_ADDR = inet_ntoa(in);
         printf("Connecting server IP address: %s\n", SERV_HOST_ADDR);
     }

     if (argc != 2)
        perror("usage: a.out <IPaddress>");

     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        perror("socket error.");


       bzero(&servaddr, sizeof(servaddr));
       servaddr.sin_family = AF_INET;
       servaddr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
       servaddr.sin_port = htons(SERV_TCP_PORT);  /* daytime server */

     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) <0 )
        perror("connect error");

     while ( (n=read(sockfd, recvline, MAXLINE)) >0 ) {
        recvline[n] = 0;
        if (fputs(recvline, stdout) == EOF)
           perror("fputs error");
     }

     if (n<0)
        perror("read error");

     exit(0);
}
 

  • Example 3: TCP Echo Server
/* Example of server using TCP protocol. */
#include "inet.h"
#define MAXLINE 512
int
readline(fd, ptr, maxlen)
register int  fd;
register char *ptr;
register int  maxlen;
/*
 * Read a line from a descriptor.  Read the line one byte at a time,
 * looking for the newline.  We store the newline in the buffer,
 * then follow it with a null (the same as fgets(3)).
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */

{
    int n, rc;
    char c;
    for (n=1; n<maxlen; n++) {
        if ((rc=recv(fd, &c, 1, 0))==1) {  
           *ptr++ = c;
           if (c=='\n')
              break;
        } else if (rc == 0) {
           if (n == 1)
              return(0);    /* EOF, no data read */
           else
              break;        /* EOF, some data was read */
        } else
              return(-1);   /* error */
    }
    *ptr = 0;
    return(n);
}
int
writen(fd, ptr, nbytes)
register int   fd;
register char  *ptr;
register int   nbytes;
/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 */

{
    int  nleft, nwritten;

    nleft = nbytes;
    while (nleft > 0) {
          nwritten = send(fd, ptr, nleft, 0);  
          if (nwritten <= 0)
               return(nwritten);   /* error */
          nleft -= nwritten;
          ptr   += nwritten;
    }
    return(nbytes - nleft);
}
str_echo(sockfd)
int sockfd;
/*
 * Read a stream socket one line at a time, and write each line back
 * to the sender.
 *
 * Return when the connection is terminated.
 */

{
    int  n;
    char line[MAXLINE];
    for (; ;) {
        n = readline(sockfd, line, MAXLINE);
        if (n==0)
            return;   /* connection terminated  */
        else if (n<0)
            perror("str_echo: readline error");

        if (writen(sockfd, line, n) != n)
            perror("str_echo: writen error");
    }
}
main(argc, argv)
int argc;
char *argv[];
{
        int                  sockfd, newsockfd, clilen, childpid;
        struct sockaddr_in   cli_addr, serv_addr;
        pname = argv[0];
        /* Open a TCP socket (an Internet stream socket). */
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
                perror("server: can't open stream socket");
        /* Bind our local address so that the client can send to us. */
        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family      = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port        = htons (SERV_TCP_PORT);
        if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
                perror("server:can't bind local address");
        listen(sockfd, 5);
        for (; ;){
               /* Wait for a connectio from a client process. */
                clilen = sizeof(cli_addr);
                newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
                                                                  &clilen);
                if (newsockfd < 0)
                      perror("server: accept error");
                if ( (childpid = fork()) <0)
                      perror("server: fork error");
                else if (childpid==0) { /* child process */
                    close(sockfd);      /* close original socket */
                    str_echo(newsockfd);  /* process the request */
                    exit(0);
                }

                close(newsockfd);        /* parent process */
        }
}
  • Example 4: TCP Echo Client
/*
 * Example of client using TCP protocol.
 */

#include "inet.h"
#define MAXLINE 512
int
readline(fd, ptr, maxlen)
register int  fd;
register char *ptr;
register int  maxlen;
/*
 * Read a line from a descriptor.  Read the line one byte at a time,
 * looking for the newline.  We store the newline in the buffer,
 * then follow it with a null (the same as fgets(3)).
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */

{
    int n, rc;
    char c;
    for (n=1; n<maxlen; n++) {
        if ((rc=recv(fd, &c, 1, 0))==1) {  
           *ptr++ = c;
           if (c=='\n')
              break;
        } else if (rc == 0) {
           if (n == 1)
              return(0);    /* EOF, no data read */
           else
              break;        /* EOF, some data was read */
        } else
              return(-1);   /* error */
    }
    *ptr = 0;
    return(n);
}
int
writen(fd, ptr, nbytes)
register int   fd;
register char  *ptr;
register int   nbytes;
/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 */

{
    int  nleft, nwritten;

    nleft = nbytes;
    while (nleft > 0) {
          nwritten = send(fd, ptr, nleft, 0);  
          if (nwritten <= 0)
               return(nwritten);   /* error */
          nleft -= nwritten;
          ptr   += nwritten;
    }
    return(nbytes - nleft);
}
str_cli(fp, sockfd)
register FILE  *fp;
register int   sockfd;
/*
 * Read the contents of the FILE *fp, write each line to the
 * stream soocket (to the server process), then read a line back from
 * the socket and write it to the standard output.
 *
 * Return to caller when an EOF is encountered on the input file.
 */

{
    int n;
    char sendline[MAXLINE], recvline[MAXLINE + 1];

    while(fgets(sendline, MAXLINE, fp) != NULL) {
        n = strlen(sendline);
        if (writen(sockfd, sendline, n)!=n)
              perror("str_cli:writen error on socket");
        /*
         * Now read a line from the socket and write it to
         * our standard output.
         */

        n = readline(sockfd, recvline, MAXLINE);
        if (n<0)
           perror("str_cli:readline error");
        recvline[n]=0;    /* null terminate */
        fputs(recvline, stdout);
    }

    if (ferror(fp))
        perror("str_cli: error reading file");
}
main(argc, argv)
int   argc;
char  *argv[];
{
        int sockfd;
        struct sockaddr_in serv_addr;
        struct hostent *hp;
        char **p;
        struct in_addr in;
        /* get the server IP address */
        hp = gethostbyname(argv[1]);
        if (hp != NULL) {
           p = hp->h_addr_list;
           memcpy(&in.s_addr, *p, sizeof(in.s_addr));
           SERV_HOST_ADDR = inet_ntoa(in);
           printf("Connecting Server IP address: %s\n", SERV_HOST_ADDR);
        }
        /*
         * Fill in the structure "serv_addr" with the address of the
         * server that we want to connect with.
         */

        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
        serv_addr.sin_port = htons(SERV_TCP_PORT);
        /*
         * Open a TCP socket (an Internet stream socket).
         */

         if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) <0)
             perror("client: can't open stream socket");
        /*
         * Connect to the server.
         */

         if (connect(sockfd, (struct sockaddr *) &serv_addr,
                                                    sizeof(serv_addr))<0)
             perror("client: can't connect to server");
         str_cli(stdin, sockfd);   /* do it all */
         close(sockfd);
         exit(0);
}

  • Example 5: UDP Echo Server
/*
 * Example of server using UDP protocol
 */


#include "inet.h"
#define MAXMESG 2048

dg_echo (sockfd, pcli_addr, maxclilen)
int  sockfd;
struct sockaddr *pcli_addr;  /* ptr to appropriate sockaddr_XX structure */
int  maxclilen;              /* sizeof(*pcli_addr) */

/*
 * Read a datagram from a connectionless socket and write it back to
 * the sender.
 *
 * We never return, as we never know when a datagram client is done.
 */


{
     int  n, clilen;
     char mesg[MAXMESG];

     for (;;) {
         clilen = maxclilen;
         n = recvfrom(sockfd, mesg, MAXMESG, 0, pcli_addr, &clilen);
         if (n<0)
             perror("dg_echo:recvfrom error");
         if (sendto(sockfd, mesg, n, 0, pcli_addr, clilen) != n)
             perror("dg_echo:sendto error");
     }
}

main(argc, argv)
int   argc;
char  *argv[];
{
        int sockfd;
        struct sockaddr_in serv_addr, cli_addr;

        /*
         * Open a UDP socket (an Internet datagram socket).
         */


        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
                perror("server: can't open datagram socket");

        /*
         * Bind our local address so that the client can send to us.
         */


        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(SERV_UDP_PORT);

        if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0)
                perror("server: can't bind local address");

        dg_echo(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr));
                /* NOTREACHED */
}
  • Example 6: UDP Echo Client
/*
 * Example of client using UDP protocol.
 */


#include  "inet.h"
#define MAXLINE 512

dg_cli(fp, sockfd, pserv_addr, servlen)
FILE   *fp;
int    sockfd;  
struct sockaddr *pserv_addr;  /* ptr to appropriate sockaddr_XX structure */
int    servlen;               /* actual sizeof(*pserv_addr) */

/*
 * Read the contents of the FILE *fp, writen each line to the
 * datagram socket, then read a line back from the datagram
 * socket and write it to the standard output.
 *
 * Return to caller when an EOF is encountered on the input file.
 */


{
    int n;
    char sendline[MAXLINE], recvline[MAXLINE + 1];

    while (fgets(sendline, MAXLINE, fp)!=NULL) {
        n = strlen(sendline);
        if (sendto(sockfd, sendline, n, 0, pserv_addr, servlen)!=n)
            perror("dg_cli: sendto error on socket");

        /*
         * Now read a message from the socket and write it to
         * our standard output.
         */


        n = recvfrom(sockfd, recvline, MAXLINE, 0,
                         (struct sockaddr *)0, (int *)0);

        if (n<0)
            perror("dg_cli: recvfrom error");

        recvline[n]=0;   /* null terminate */
        fputs(recvline, stdout);
    }

    if (ferror(fp))
        perror("dg_cli: error reading file");
}

main(argc, argv)
int   argc;
char  *argv[];
{
    int sockfd;
    struct sockaddr_in cli_addr, serv_addr;
    struct hostent *hp;
    char **p;
    struct in_addr in;

    hp = gethostbyname(argv[1]);
    if (hp != NULL) {
       p = hp->h_addr_list;
       memcpy(&in.s_addr, *p, sizeof(in.s_addr));
       SERV_HOST_ADDR = inet_ntoa(in);
       printf("Connecting Server IP address: %s\n", SERV_HOST_ADDR);
    }

    /*
     * Fill in the structure "serv_addr" with the address of the
     * server that we want to send to.
     */


    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
    serv_addr.sin_port = htons(SERV_UDP_PORT);

    /*
     * Open a UDP socket (an Internet datagram socket).
     */


    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) <0)
        perror("client: can't open datagram socket");

    /*
     * Bind any local address for us.
     */


    bzero((char *) &cli_addr, sizeof(cli_addr)); /* zero out */
    cli_addr.sin_family = AF_INET;
    cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    cli_addr.sin_port = htons(CLI_UDP_PORT);
    if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) <0)
       perror("client: can't bind local address");

    dg_cli(stdin, sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

    close(sockfd);
    exit(0);
}

  • A necessary header file: inet.h
/*
 * Definitions for TCP and UDP client/server programs.
 */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>     /* for struct hostent */
#define SERV_UDP_PORT 51000
#define SERV_TCP_PORT 52000
#define CLI_UDP_PORT 50000
char *pname;
char *SERV_HOST_ADDR = NULL;

Tools

Thanks for Reading

If you would rather like to have this lecture note in printed format, please click the print action link in the top right corner.

If you find any problem in this lecture note, please feel free to tell Steven via steven@findaway.hk.

Edit - History - Print - Recent Changes - Search
Page last modified on November 25, 2009, at 07:59 PM