[CS:APP-ch11] Network Programming

CS:APP-ch11 Network Programming

11.1 The Client-Server Programming Model

4 steps of client-server transaction

11.2 Networks

a hierarchical system that is organized by geographical proximity.

lowest level: LAN (Ethernet)

higher level: WAN

internet

consist of different LANs and WANs with radically different and incompatible technologies.

a layer of protocol software running on each host and router that smoothes out the differences between the different networks. (network layer)

step 5 : router strips off the old LAN1 frame header and prepends a new LAN2 frame header.

11.3 The Global IP Internet

11.3.1 IP Addresses

an unsigned 32-bit integer

1
2
3
4
/* Internet address structure */
struct in_addr {
	unsigned int s_addr; /* Network byte order (big-endian) */
};

Addresses in IP address structures are always stored in (big-endian) network byte order, even if the host byte order is little-endian.

convert between network and host byte order

1
2
3
4
5
6
7
#include <netinet/in.h>
unsigned long int htonl ( unsigned long int hostlong );
unsigned short int htons ( unsigned short int hostshort );
										// Returns : value in network byte order
unsigned long int ntohl ( unsigned long int netlong );
unsigned short int ntohs ( unsigned short int netshort );
										// Returns : value in host byte order

convert between IP addresses and dotted- decimal strings

1
2
3
#include <arpa/inet.h>
int inet_pton ( AF_INET, const char *src, void *dst );// dst = struct in_addr
const char *inet_ntop( AF_INET, const void *src, char *dst, socklen_t size );

11.3.2 Internet Domain names

nslookup

11.3.3 Internet Connections

socket address: ip addr : port


11.4 The Sockets Interface

socket address structure

1
2
3
4
5
6
7
8
9
10
11
12
13
/* IP socket address structure */
struct sockaddr_in {
    uint16_t sin_family;         /* Protocol family (always AF_INET) */
    uint16_t sin_port;           /* Port number in network byte order */
    struct in_addr sin_addr;     /* IP address in network byte order */
    unsigned char sin_zero[ 8 ]; /* Pad to sizeof ( struct sockaddr ) */
};

/* Generic socket address structure (for connect, bind, and accept) */
struct sockaddr {
    uint16_t sa_family; /* Protocol family */
    char sa_data[ 14 ]; /* Address data. */
};

socket

1
2
3
4
#include <sys/types.h>
#include <sys/socket.h>

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

connect

1
int connect( int clientfd, const struct sockaddr *addr, socklen_t addrlen );

bind: associate server’s socket address with socketfd

1
int bind( int sockfd, const struct sockaddr *addr, socklen_t addrlen );

listen: convert to listening socket that accept connections

1
int listen( int sockfd, int backlog );

accept

1
int accept ( int listenfd, struct sockaddr *addr, int *addrlen );

struct addrinfo

1
2
3
4
5
6
7
8
9
10
struct addrinfo {
    int ai_flags;             /*hints argument flags*/
    int ai_family;            /*first arg to socket fun*/
    int ai_socktype;          /*second arg*/
    int ai_protocol;          /*third arg*/
    char *ai_canonname;       /*canonical host name*/
    size_t ai_addrlen;        /*size of ai_addr struct*/
    struct sockaddr *ai_addr; /*ptr to socket address structure*/
    struct adrinfo *ai_netx;  /*ptr to next item in linked list*/
};

convert between binary socket address structures and hostname strings

getaddrinfo

1
2
3
4
5
6
7
8
9
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo( const char *host, const char *service, 
				const struct addrinfo *hints, struct addrinfo **result );

void freeaddrinfo( struct addrinfo *result );
const char *gai_strerror( int errcode );

getnameinfo

1
2
3
int getnameinfo( const struct sockaddr *sa, socklen_t salen, 
				char *host, size_t hostlen,
				char *service, size_t servlen, int flags );

usage: combine all

open_clientfd ( getaddrinfo, socket, connect, freeaddrinfo )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 * open_clientfd - Open connection to server at <hostname, port> and
 *     return a socket descriptor ready for reading and writing. This
 *     function is reentrant and protocol-independent.
 *
 *     On error, returns:
 *       -2 for getaddrinfo error
 *       -1 with errno set for other errors.
 */
/* $begin open_clientfd */
int open_clientfd ( char *hostname, char *port ) {
    int clientfd, rc;
    struct addrinfo hints, *listp, *p;

    /* Get a list of potential server addresses */
    memset ( &hints, 0, sizeof ( struct addrinfo ) );
    hints.ai_socktype = SOCK_STREAM; /* Open a connection */
    hints.ai_flags = AI_NUMERICSERV; /* ... using a numeric port arg. */
    hints.ai_flags |= AI_ADDRCONFIG; /* Recommended for connections */
    if ( ( rc = getaddrinfo ( hostname, port, &hints, &listp ) ) != 0 ) {
        fprintf ( stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror ( rc ) );
        return -2;
    }

    /* Walk the list for one that we can successfully connect to */
    for ( p = listp; p; p = p->ai_next ) {
        /* Create a socket descriptor */
        if ( ( clientfd = socket ( p->ai_family, p->ai_socktype, p->ai_protocol ) ) < 0 )
            continue; /* Socket failed, try the next */

        /* Connect to the server */
        if ( connect ( clientfd, p->ai_addr, p->ai_addrlen ) != -1 )
            break; /* Success */
        if ( close ( clientfd ) <
             0 ) { /* Connect failed, try another */ // line:netp:openclientfd:closefd
            fprintf ( stderr, "open_clientfd: close failed: %s\n", strerror ( errno ) );
            return -1;
        }
    }

    /* Clean up */
    freeaddrinfo ( listp );
    if ( !p ) /* All connects failed */
        return -1;
    else /* The last connect succeeded */
        return clientfd;
}
/* $end open_clientfd */

open_listenfd (getaddrinfo, socket, bind, listen, freeaddrinfo)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
 * open_listenfd - Open and return a listening socket on port. This
 *     function is reentrant and protocol-independent.
 *
 *     On error, returns:
 *       -2 for getaddrinfo error
 *       -1 with errno set for other errors.
 */
/* $begin open_listenfd */
int open_listenfd ( char *port ) {
    struct addrinfo hints, *listp, *p;
    int listenfd, rc, optval = 1;

    /* Get a list of potential server addresses */
    memset ( &hints, 0, sizeof ( struct addrinfo ) );
    hints.ai_socktype = SOCK_STREAM;             /* Accept connections */
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
    hints.ai_flags |= AI_NUMERICSERV;            /* ... using port number */
    if ( ( rc = getaddrinfo ( NULL, port, &hints, &listp ) ) != 0 ) {
        fprintf ( stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror ( rc ) );
        return -2;
    }

    /* Walk the list for one that we can bind to */
    for ( p = listp; p; p = p->ai_next ) {
        /* Create a socket descriptor */
        if ( ( listenfd = socket ( p->ai_family, p->ai_socktype, p->ai_protocol ) ) < 0 )
            continue; /* Socket failed, try the next */

        /* Eliminates "Address already in use" error from bind */
        setsockopt ( listenfd, SOL_SOCKET, SO_REUSEADDR, // line:netp:csapp:setsockopt
                     (const void *)&optval, sizeof ( int ) );

        /* Bind the descriptor to the address */
        if ( bind ( listenfd, p->ai_addr, p->ai_addrlen ) == 0 )
            break;                      /* Success */
        if ( close ( listenfd ) < 0 ) { /* Bind failed, try the next */
            fprintf ( stderr, "open_listenfd close failed: %s\n", strerror ( errno ) );
            return -1;
        }
    }

    /* Clean up */
    freeaddrinfo ( listp );
    if ( !p ) /* No address worked */
        return -1;

    /* Make it a listening socket ready to accept connection requests */
    if ( listen ( listenfd, LISTENQ ) < 0 ) {
        close ( listenfd );
        return -1;
    }
    return listenfd;
}
/* $end open_listenfd */

client routine (close)

1
2
3
4
5
6
7
8
9
clientfd = Open_clientfd ( host, port );
Rio_readinitb ( &rio, clientfd );
while ( Fgets ( buf, MAXLINE, stdin ) != NULL ) { // read from std input
    Rio_writen ( clientfd, buf, strlen ( buf ) ); // write to server
    Rio_readlineb ( &rio, buf, MAXLINE );         // read from server
    Fputs ( buf, stdout );
}
Close ( clientfd );
exit ( 0 );

server routine (accept, close)

1
2
3
4
5
6
7
8
9
10
listenfd = Open_listenfd ( argv[ 1 ] );
while ( 1 ) {
    // protocol independent socket address structure rather than sockaddr_in
    clientlen = sizeof ( struct sockaddr_storage ); 
    connfd = Accept ( listenfd, (SA *)&clientaddr, &clientlen ); // SA = sockaddr
    
    echo ( connfd );
    Close ( connfd );
}
exit ( 0 );

11.5 Web Servers

Web clients and servers interact using a text-based application-level protocol known as HTTP (Hypertext Transfer Protocol).

web content: static & dynamic, disk file & output of an executable file

URL: server : port / filename ? arg1 & arg2

  • http://bluefish.ics.cs.cmu.edu:8000/cgi-bin/adder?15000&213

http transaction

  • use telnet to conduce transactions with any web server

http request: method URI version

  • GET /index.html HTTP/1.0

http response: version status-code status-message

  • HTTP/1.0 404 Not found

dynamic content

  • fork and execute a child process , redirect stdout to client

11.6 Putting It Together: The Tiny Web Server