Introduction to the Socket API

Tom Kelliher, CS43

Mar. 19, 1996

Background reading: W. R. Stevens, UNIX Network Programming, Chapter 6 (on reserve).

A Client-Server Dialogue

Sockets API

socket()

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

Used to create an unamed socket. Must bind or connect.

Returns a socket descriptor (small positive int). Returns -1 on error.

domain:

type:

protocol: set to 0.

Typical call:

int sock;

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
   die();

bind()

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sd, const struct sockaddr *name, int namelen);

Assigns a name (port, etc.) to a socket.

Returns 0 if successful, otherwise -1 and look in errno.

sd: socket descriptor.

name:

namelen: sizeof name structure.

Typical call:

struct sockaddr_in server;

bzero((char *) &server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(SERVER_PORT);
if (bind(sock, (struct sockaddr *) &server, sizeof(server)))
   die();

listen()

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sd, int backlog);

Announce willingness to accept connections to socket and a maximum queue length.

Returns 0 on success, otherwise -1.

sd: socket descriptor.

backlog: max size of queue. Maximum is 5.

Typical call:

int sock;

listen(sock, 5);

accept()

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sd, struct sockaddr *addr, int *addrlen);

Extract first connection request on queue; blocking if queue is empty.

Returns -1 on error, otherwise a non-negative descriptor to use for socket I/O ( read() or write()).

sd: socket descriptor.

addr:

addrlen: addressof sizeof addr structure.

Typical call:

struct sockaddr_in client;
int msgsock, sock, clientLen;

clientLen = sizeof(client);
if ((msgsock = accept(sock, (struct sockaddr *) &client,
                      &clientLen)) == -1)
   die();
else {
   /* Print information about the client. */
   if (clientLen != sizeof(client))
      die();

   printf("Client IP: %s\n", inet_ntoa(client.sin_addr));
   printf("Client Port: %hu\n", ntohs(client.sin_port));

connect()

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sd, const struct sockaddr *name, int namelen);

Connect socket to another socket, opening a socket pair communication channel.

Returns 0 on success, -1 otherwise. Check errno.

sd: socket descriptor.

name:

namelen: sizeof name structure.

Will automatically do a bind and assign an ephemeral port. Use getsockname() to discover the port number.

Typical call:

struct sockaddr_in server;
struct hostent* hp;

bzero((char *) &server, sizeof(server));
server.sin_family = AF_INET;
if ((hp = gethostbyname(argv[1])) == NULL) {
   sprintf(buf, "%s: unknown host\n", argv[1]);
   die(buf);
}
bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
server.sin_port = htons((u_short) SERVER_PORT);

/* Try to connect */
if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
   pdie("Connecting stream socket");

/* Determine what port client's using. */
clientLen = sizeof(client);
if (getsockname(sock, (struct sockaddr *) &client, &clientLen))
   pdie("Getting socket name");

if (clientLen != sizeof(client))
   die("getsockname() overwrote name structure");

printf("Client socket has port %hu\n", ntohs(client.sin_port));

read()

#include <unistd.h>

ssize_t read(int sd, void* buf, size_t nbytes);

Read at most nbytes bytes into buf from sd.

Returns -1 on error, 0 on EOF (connection closed), or the actual number of bytes read.

sd: socket descriptor.

buf: addressof char buffer.

nbytes: sizeof buf.

Typical call:

char buf[BUFFER_SIZE];
int rval, msgsock;

bzero(buf, BUFFER_SIZE);
if ((rval = read(msgsock, buf, BUFFER_SIZE)) < 0)
   die();

write()

#include <unistd.h>

ssize_t write(int sd, const void *buf, size_t nbytes);

Attempts to write nbytes from buf to sd.

Returns -1 on error or the number of bytes actually written.

Arguments similar to read().

close()

#include <unistd.h>

int close(int sd);

Close socket descriptor sd.

Returns -1 on failure, otherwise 0.

Byte Ordering Calls

#include <sys/param.h>

u_long  htonl(u_long hostlong);
u_short htons(u_short hostshort);
u_long  ntohl(u_long netlong);
u_short ntohs(u_short netshort);

Convert 16- and 32-bit integers between native format (host) and network format.

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

unsigned long inet_addr(const char *ip);
char* inet_ntoa(struct in_addr in);

inet_addr() converts a char string IP address to its 32-bit network byte-order integer equivalent.

inet_ntoa does the opposite.

Example Server Program

/**********************************************************************
 * server.c --- Demonstrate a simple iterative server.
 * Tom Kelliher
 *
 * This program demonstrates a simple iterative server.  The server
 * opens a TCP connection on port SERVER_PORT and begins accepting
 * connections from anywhere.  It sits in an endless loop, so one must
 * send an INTR to terminate it.
 *
 * The server reads a message from the client, printing it to stdout.
 * Then, the server sends a simple message back to the client.
 **********************************************************************/


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>


#define DATA "Danger Will Roger . . ."
#define TRUE 1
#define SERVER_PORT 5001
#define BUFFER_SIZE 1024


/* prototypes */
void die(const char *);
void pdie(const char *);


/**********************************************************************
 * main
 **********************************************************************/

int main(void) {

   int sock;   /* fd for main socket */
   int msgsock;   /* fd from accept return */
   struct sockaddr_in server;   /* socket struct for server connection */
   struct sockaddr_in client;   /* socket struct for client connection */
   int clientLen;   /* returned length of client from accept() */
   int rval;   /* return value from read() */
   char buf[BUFFER_SIZE];   /* receive buffer */

   /* Open a socket, not bound yet.  Type is Internet TCP. */
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      pdie("Opening stream socket");

   /*
      Prepare to bind.  Permit Internet connections from any client
      to our SERVER_PORT.
   */
   bzero((char *) &server, sizeof(server));
   server.sin_family = AF_INET;
   server.sin_addr.s_addr = INADDR_ANY;
   server.sin_port = htons(SERVER_PORT);
   if (bind(sock, (struct sockaddr *) &server, sizeof(server)))
      pdie("Binding stream socket");

   printf("Socket has port %hu\n", ntohs(server.sin_port));

   /* Set the listen queue to 5, the maximum. */
   listen(sock, 5);

   /* Loop, waiting for client connections. */
   /* This is an interactive server. */
   while (TRUE) {

      clientLen = sizeof(client);
      if ((msgsock = accept(sock, (struct sockaddr *) &client,
                            &clientLen)) == -1)
         pdie("Accept");
      else {
         /* Print information about the client. */
         if (clientLen != sizeof(client))
            pdie("Accept overwrote sockaddr structure.");

         printf("Client IP: %s\n", inet_ntoa(client.sin_addr));
         printf("Client Port: %hu\n", ntohs(client.sin_port));

         do {   /* Read from client until it's closed the connection. */
            /* Prepare read buffer and read. */
            bzero(buf, sizeof(buf));
            if ((rval = read(msgsock, buf, BUFFER_SIZE)) < 0)
               pdie("Reading stream message");

            if (rval == 0)   /* Client has closed the connection */
               fprintf(stderr, "Ending connection\n");
            else
               printf("S: %s\n", buf);

            /* Write back to client. */
            if (write(msgsock, DATA, sizeof(DATA)) < 0)
               pdie("Writing on stream socket");

         } while (rval != 0);
      }   /* else */

      close(msgsock);
   }

   exit(0);

}


/**********************************************************************
 * pdie --- Call perror() to figure out what's going on and die.
 **********************************************************************/

void pdie(const char *mesg) {

   perror(mesg);
   exit(1);
}


/**********************************************************************
 * die --- Print a message and die.
 **********************************************************************/

void die(const char *mesg) {

   fputs(mesg, stderr);
   fputc('\n', stderr);
   exit(1);
}

Example Client Program

/**********************************************************************
 * client.c --- Demonstrate a simple client.
 * Tom Kelliher
 *
 * This program will connect to a simple iterative server and exchange
 * messages.  The single command line argument is the server's hostname.
 * The server is expected to be accepting connection requests from
 * SERVER_PORT.
 *
 * The same message is sent three times over separate connections, 
 * demonstrating that different ephemeral ports are used for each
 * connection.
 **********************************************************************/


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>


#define DATA "The sea is calm tonight, the tide is full . . ."
#define SERVER_PORT 5001
#define BUFFER_SIZE 1024


/* prototypes */
void die(const char *);
void pdie(const char *);


/**********************************************************************
 * main
 **********************************************************************/

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

   int sock;   /* fd for socket connection */
   struct sockaddr_in server;   /* Socket info. for server */
   struct sockaddr_in client;   /* Socket info. about us */
   int clientLen;   /* Length of client socket struct. */
   struct hostent *hp;   /* Return value from gethostbyname() */
   char buf[BUFFER_SIZE];   /* Received data buffer */
   int i;   /* loop counter */

   if (argc != 2)
      die("Usage: client hostname");

   /* Open 3 sockets and send same message each time. */

   for (i = 0; i < 3; ++i)
   {
      /* Open a socket --- not bound yet. */
      /* Internet TCP type. */
      if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
         pdie("Opening stream socket");
      
      /* Prepare to connect to server. */
      bzero((char *) &server, sizeof(server));
      server.sin_family = AF_INET;
      if ((hp = gethostbyname(argv[1])) == NULL) {
         sprintf(buf, "%s: unknown host\n", argv[1]);
         die(buf);
      }
      bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
      server.sin_port = htons((u_short) SERVER_PORT);
      
      /* Try to connect */
      if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
         pdie("Connecting stream socket");
      
      /* Determine what port client's using. */
      clientLen = sizeof(client);
      if (getsockname(sock, (struct sockaddr *) &client, &clientLen))
         pdie("Getting socket name");
      
      if (clientLen != sizeof(client))
         die("getsockname() overwrote name structure");
      
      printf("Client socket has port %hu\n", ntohs(client.sin_port));
      
      /* Write out message. */
      if (write(sock, DATA, sizeof(DATA)) < 0)
         pdie("Writing on stream socket");
      
      /* Prepare our buffer for a read and then read. */
      bzero(buf, sizeof(buf));
      if (read(sock, buf, BUFFER_SIZE) < 0)
         pdie("Reading stream message");
      
      printf("C: %s\n", buf);
      
      /* Close this connection. */
      close(sock);
   }

   exit(0);

}


/**********************************************************************
 * pdie --- Call perror() to figure out what's going on and die.
 **********************************************************************/

void pdie(const char *mesg) {

   perror(mesg);
   exit(1);
}


/**********************************************************************
 * die --- Print a message and die.
 **********************************************************************/

void die(const char *mesg) {

   fputs(mesg, stderr);
   fputc('\n', stderr);
   exit(1);
}



Thomas P. Kelliher
Mon Mar 18 22:35:52 EST 1996
Tom Kelliher