Unix Network Programing in C
Unix Network Programing in C
Now available on most platforms: Linux, MacOS X, Windows, FreeBSD, Solaris, etc. Largely compatible cross-platform
Recommended reading:
Stevens, Fenner, and Rudoff, Unix Network Programming volume 1: The Sockets Networking API, 3rd Edition, Addison-Wesley, 2003.
Concepts
Application
Socket
Network
The port is a 16-bit number used to distinguish servers E.g. web server listens on port 80, email server on port 25
Once connection is established, either side can write data into the connection, where it becomes available for the other side to read
TCP/IP Connection
Client
fd fd connfd
Server
Socket
Network
? ?
Socket
int fd = socket(...)
connect(fd, ..., ...) write(fd, data, datalen) read(fd, buffer, buflen) close(fd) 5
connfd = accept(fd, ...) read(connfd, buffer, buflen) write(connfd, data, datalen) close(connfd)
TCP/IP Connection
Server Client
fd = socket(); connect(fd, ); write(fd, ); read(fd, ); close(fd, );
TCP/IP connection established
fd = socket(); bind(fd, );
Specify well-known port Begin listening
listen(fd, );
Creating a socket
#include <sys/socket.h> <sys/types.h> #include <sys/socket.h> int fd; ... fd = socket(family, type, protocol); if (fd == -1) { // Error: unable to create socket ... } ...
AF_INET for IPv4 AF_INET6 for IPv6 SOCK_STREAM for TCP SOCK_DGRAM for UDP 0 (not used for Internet sockets)
Create an unbound socket, not connected to network; can be used as either a client or a server
7
Handling Errors
Socket functions return -1 and set the global integer variable errno on failure
fd = socket(family, type, protocol); if (fd == -1) { // Error occurred; look at // errno to determine what // to do. ... }
The Unix man pages list possible errors that can occur for each function E.g. do man 2 socket in a terminal, and read the ERRORS section
Needed to run servers on a well-known port with addr specified as INADDR_ANY Not generally used on clients, since typically dont care which port used
#include <sys/types.h> #include <sys/socket.h> ... if (bind(fd, addr, addrlen) == -1) { // Error: unable to bind ... } ...
Tell the socket to listen for new connections The backlog is the maximum number of connections the socket will queue up, each waiting to be accept()ed
10
Connecting to a Server
#include <sys/types.h> #include <sys/socket.h> if (connect(fd, addr, addrlen) == -1) { // Error: unable to open connection ... } ... Pointer to a struct sockaddr Size of the struct in bytes
Tries to open a connection to the server Times out after 75 seconds if no response
11
Must specify the address and port when calling bind() or connect()
The address can be either IPv4 or IPv6 Could be modelled in C as a union, but the designers of the sockets API chose to use a number of structs, and abuse casting instead
12
struct sockaddr
Has a data field big enough to hold the largest address of any family Plus sa_len and sa_family to specify the length and type of the address Treats the address as an opaque binary string
13
struct sockaddr_in
s_addr;
struct sockaddr_in uint8_t Use struct sockaddr_in to sa_family_t hold an IPv4 address in_port_t struct in_addr Has the same size and memory char layout as struct sockaddr, but interprets the bits differently }; to give structure to the address
14
struct sockaddr_in6
s6_addr[16];
struct sockaddr_in6 uint8_t Use struct sockaddr_in6 sa_family_t to hold an IPv6 address in_port_t uint32_t Has the same size and memory struct in6_addr layout as struct sockaddr, but interprets the bits differently }; to give structure to the address
15
struct sockaddr_in6
Cast it to a struct sockaddr before calling the socket routines
struct sockaddr_in addr; ... // Fill in addr here ... if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { ...
16
Servers often just want to listen on the default address do this using INADDR_ANY for the address passed to bind() Convert port number using htons()
struct sockaddr_in addr; ... addr.sin_addr.s_addr = INADDR_ANY; addr.sin_family = AF_INET; addr.sin_port = htons(80); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { ...
17
Clients want to connect to a specific IP address can use inet_pton() to create the address, if you know the numeric IP address Convert port number using htons()
struct sockaddr_in addr; ... inet_pton(AF_INET, 130.209.240.1, &addr.sin_addr); addr.sin_family = AF_INET; addr.sin_port = htons(80); if
D T (connect(fd, N ... DO
IS TH O
US
TH E
DN E
IN S
AD TE S
18
Use getaddrinfo() to look-up name in DNS Returns a linked list of struct addrinfo values, representing addresses of the host
struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; };
// // // // // // // //
input flags AF_INET, AF_INET6, ... IPPROTO_TCP, IPPROTO_UDP SOCK_STREAM, SOCK_DRAM, ... length of socket-address socket-address for socket canonical name of host pointer to next in list
19
20
Accepting Connections
#include <sys/types.h> #include <sys/socket.h> int connfd; struct sockaddr_in cliaddr; socklen_t cliaddrlen = sizeof(cliaddr); ... connfd = accept(fd, (struct sockaddr *) &cliaddr, &cliaddrlen); if (connfd == -1) { // Error ... } ... Accepts a connection, returns new file descriptor for the connection (connfd) and client address (cliaddr)
21
Accepting Connections
Can accept() connections one at a time, handling each request in series Can accept() connections and start a new thread for each, allowing it to process several in parallel
22
The read() call reads up to BUFLEN bytes of data from connection blocks until data available Returns actual number of bytes read, or -1 on error Data is not null terminated
23
} if (FD_ISSET(fd2, &rfds)) { ... // Data available to read on fd2 } } if (rc < 0) ... // error 24
The write() call sends data over a socket; blocks until all data can be written Returns actual number of bytes written, or -1 on error
25
sprintf(y, "Hello, world!"); printf("x = %s\n", x); printf("y = %s\n", y); printf("sizeof(x) = %d\n", sizeof(x)); printf("sizeof(y) = %d\n", sizeof(y)); printf("strlen(x) = %d\n", strlen(x)); printf("strlen(y) = %d\n", strlen(y)); return 0; } 26
Why?
Closing a Socket
#include <unistd.h> close(fd);
27
Questions?
28