CMPSC 311, Introduction to Systems Programming

Introduction to Sockets



Reading



Network Programming with Sockets
A simple point-to-point model



Client-Server Model for application design
Service Transaction
Server design
Client design



Networks - host, connector, "wire"
IP Network - a specific way to identify hosts and exchange data

IP Internet - same but more general
Protocol = rules for exchanging data
Naming scheme
Delivery mechanism



Internet Protocol, building the Global IP Network in layers
Recall,
IP provides naming and delivery of packets (datagrams)
UDP = Unreliable Datagram Protocol
TCP provides reliable delivery of packets



process --- host --- connection --- host --- process



IPv4 (Internet Protocol version 4), 32-bit IP address
IPv6, 128-bit IP address

A host is mapped to an IP address (numeric).
An IP address is mapped to an Internet Domain Name (symbolic).

Internet Address (IPv4)
Internet Domain Name
Lookup Functions (old)
#include <netdb.h>
struct hostent * gethostbyaddr(const void *addr, socklen_t len, int type);

struct hostent * gethostbyname(const char *name);

The return values are pointers to static data.

struct hostent {
  name, domain name
  aliases, NULL-terminated array of domain names
  address type (AF_INET, address family, Internet)
  address length
  addresses, NULL-terminated array of struct in_addr *
};

// Mac OS X

struct hostent {
  char *h_name;        /* official name of host */
  char **h_aliases;    /* alias list */
  int  h_addrtype;     /* host address type */
  int  h_length;       /* length of address */
  char **h_addr_list;  /* list of addresses from name server */
};

// example usage, but not complete

struct in_addr addr;      // internet address
addr.s_addr = something;  //
unsigned int

struct hostent *hostp;
hostp = gethostbyaddr(&addr, sizeof(addr), AF_INET);

printf("%s\n", hostp->h_name);

Lookup Function (new)

int getaddrinfo(const char * restrict nodename,
                const char * restrict servname,
                const struct addrinfo * restrict hints,
                struct addrinfo ** restrict res);

     struct addrinfo {
       int ai_flags;             /* input flags */
       int ai_family;            /* protocol family for socket */
       int ai_socktype;          /* socket type */
       int ai_protocol;          /* protocol for socket */
       socklen_t ai_addrlen;     /* length of socket-address */
       struct sockaddr *ai_addr; /* socket-address for socket */
       char *ai_canonname;       /* canonical name for service location */
       struct addrinfo *ai_next; /* pointer to next in list */
     };

ai_family, ai_socktype, and ai_protocol can be used later in a call to socket().
For each addrinfo structure in the list, the ai_addr member points to a filled-in socket address structure of length ai_addrlen.
Internet Connections
Socket = connection endpoint
Connection = two socket addresses
Sockets overview, as a sequence of function calls
Sockets
struct sockaddr, generic socket address, 16 bytes
protocol family, 2 bytes, indicates meaning of additional data
additional data, 14 bytes

struct sockaddr_in, Internet-style socket address, 16 bytes
address family, always AF_INET
port number, 16 bits (a server's port numbers are advertised)
IP address, 32 bits (IPv4)
padding, 8 bytes

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


int socket(int domain, int type, int protocol);
int connect(int sockfd, struct sockaddr *server_addr, int addrlen);
int bind(int sockfd, struct sockaddr *server_addr, int addrlen);
int listen(int sockfd, int backlog);
int accept(int listenfd, struct sockaddr *addr, int addrlen);
client
server



Summary

client-server diagram

Client
Server
The point-to-point client-server socket connection is between clientfd (in the client) and connfd (in the server).  This connection is ephemeral.



Example, an iterative echo server
Client
Server



Example, a concurrent echo server based on processes
#include "csapp.h"

void sigchld_handler(int sig)
{
while (waitpid(-1, 0, WNOHANG) > 0)
;
return;
}

void echo(int connfd); // see above, echo.c

int main(int argc, char *argv[])
{
int listenfd, connfd, port;
int clientlen = sizeof(struct sockaddr_in);
struct sockaddr_in clientaddr;

if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
port = atoi(argv[1]);

Signal(SIGCHLD, sigchld_handler);
listenfd = Open_listenfd(port);

while (1) {
connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
if (Fork() == 0) {
Close(listenfd); /* Child closes its listening socket */
echo(connfd); /* Child services client */
Close(connfd); /* Child closes connection with client */
exit(0); /* Child exits */
}
Close(connfd); /* Parent closes connected socket (important!) */
}
}



Example, a concurrent echo server based on threads
#include "csapp.h"

void echo(int connfd); // see above, echo.c
void *thread(void *vargp);

int main(int argc, char *argv[])
{
int listenfd, *connfdp, port;
int clientlen = sizeof(struct sockaddr_in);
struct sockaddr_in clientaddr;
pthread_t tid;

if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
port = atoi(argv[1]);

listenfd = Open_listenfd(port);

while (1) {
connfdp = Malloc(sizeof(int));
*connfdp = Accept(listenfd, (SA *) &clientaddr, &clientlen);
Pthread_create(&tid, NULL, thread, connfdp);
}
}

/* thread routine */
void *thread(void *vargp)
{
int connfd = *((int *)vargp);
Pthread_detach(pthread_self());
Free(vargp);
echo(connfd);
Close(connfd);
return NULL;
}



Example, adapted from Mac OS X man page getaddrinfo(3)

The following code tries to connect to host "www.kame.net" and service "http" via a stream socket.  It loops through all the addresses available, regardless of address family.  If the destination resolves to an IPv4 address, it will use an AF_INET socket.  Similarly, if it resolves to IPv6, an AF_INET6 socket is used.  Observe that there is no hardcoded reference to a particular address family.  The code works even if getaddrinfo() returns addresses that are not IPv4/v6.

struct addrinfo hints, *res;

memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

int error = getaddrinfo("www.kame.net", "http", &hints, &res);
if (error)
  {

    errx(1, "%s", gai_strerror(error));
      // error reporting and exit

  }

int s = -1;
const char *cause = NULL;

for (struct addrinfo *p = res; p != NULL; p = p->ai_next)
  {

    s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (s < 0)
      {

        cause = "socket";
        continue;
      }

    if (connect(s, p->ai_addr, p->ai_addrlen) < 0)
      {

        cause = "connect";
        close(s);
        s = -1;
        continue;
      }

      break;  /* okay, we got one */
  }

 
if (s < 0)
  {

    err(1, "%s", cause);
   
  // error reporting and exit
  }

freeaddrinfo(res);




Last revised, 14 May 2012