Internettechnologien (CS262) Socket Programming in C 4. März 2015 Christian Tschudin (basierend auf einem Foliensatz von C. Jelger und T. Meyer) Departement Mathematik und Informatik, Universität Basel Introduction What is a socket? A socket is a bi-directional communication abstraction (called association) between two processes. A socket is the endpoint of a communication. Sockets is an Application Programming Interface (API) for InterProcess Communication (IPC) process 1 process 2 socket socket e.g. web browser (Mozilla, Internet Explorer) e.g. web 2
Introduction There are may types of sockets SOCK_STREAM: TCP (connection oriented, guaranteed delivery) SOCK_DGRAM: UDP (datagram-based communication) SOCK_RAW: allows access to the network layer. This can be used to build ICMP messages or custom IP packets. SOCK_PACKET: allows access to the link-layer (e.g. Ethernet). This can be used to build entire frames (e.g. to build a user-space router). 3 Introduction There are many domains and (P)rotocol (F)amilies to deal with PF_INET: IP version 4 (32-bit addresses) PF_INET6: IP version 6 (128-bit addresses) PF_UNIX: interprocess communication on the local machine PF_APPLETALK: Appletalk networks PF_IPX: Novell Netware networks... PF_ROUTE: access to routing tables (*BSD systems) 4
Introduction 192.168.1.119 131.152.227.232 Web browser Port 32816 HTTP Port 80 PF_INET, SOCK_STREAM (IPv4) (TCP) Want to check active sockets on your machine? # netstat -ap -A inet (or inet6, or unix) Active Internet connections (s and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 *:ipp *:* LISTEN 1437/cupsd tcp 0 192.168.1.119:32816 www.cs.unibas.ch:www ESTABLISHED1621/mozilla-thunde tcp 0 192.168.1.119:32815 www.cs.unibas.ch:www ESTABLISHED1621/mozilla-thunde tcp 0 192.168.1.119:32769 clarinet.u-strasb:imaps ESTABLISHED1621/mozilla-thunde tcp 0 *:bootpc *:* 1271/dh udp 0 *:ipp *:* 1437/cupsd udp 0 *:icmp *:* 1720/vmnet-natd 5 Socket creation int socket (int domain, int type, int protocol); domain: PF_INET, PF_INET6, PF_UNIX,... type: SOCK_DGRAM, SOCK_STREAM,... protocol: 0=default, or IPPROTO_UDP, IPPROTO_TCP, The returned value is a descriptor (i.e., an integer value that uniquely identifies the socket among other descriptors such as sockets, files, pipes, etc.) Example: int my_socket; my_socket = socket(af_inet, SOCK_STREAM, 0); 6
Socket address configuration struct sockaddr_in { short int sin_family; /* domain*/ unsigned short int sin_port; /* port number */ struct in_addr sin_addr; /* IP address */ unsigned char sin_zero[8]; /* padding with zeros */}; struct in_dadr { unsigned long s_addr; }; Purpose of structure: easy access to socket parameters Basic structure: sockaddr But one should always use the appropriate sockaddr_xxx when accessing a specific structure type (e.g. sockaddr_in) sockaddr sockaddr_in family blob family port addr zero 7 Watch the conversions!!! Convert multibyte integers 50966 in decimal = c734 in hexadecimal there are two ways to encode this number in 32 bits: 34-c7-00-00 = host byte order (little-endian, Intel) 00-00-c7-34 = host byte order (big-endian, Motorola, IBM) memory n...n+3 00-00-c7-34 = network byte order (big-endian, for all machines!) sin_port and sin_addr MUST be in network byte order When building a packet (SOCK_RAW or SOCK_PACKET), all data fields must be in network byte order! 8
Integer conversion functions htonl host to network long (to convert a long integer (32 bit) from host to network byte order, before tx) htons host to network short (to convert a short integer (16 bit) from host to network byte order, before tx) ntohl network to host long (after rx) ntohs network to host short (after rx) Caution: Don t convert chars! Example: struct sockaddr_in webyahooaddr; webyahooaddr.sin_family = AF_INET; webyahooaddr.sin_port = htons(80); webyahooaddr.sin_addr.s_addr = inet_addr( 217.12.3.11 ); memset(&webyahooaddr.sin_zero, \0, 8); 9 Datagram-based communication Function calls with UDP (SOCK_DGRAM) station 1 station 2 sendto()/recvfrom() data sendto()/recvfrom() 10
Binding of the socket (to an address) Bind = Assign IP address and port number to socket Example: int mysock; struct sockaddr_in locaddr; mysock = socket(pf_inet, SOCK_STREAM, 0); /* or SOCK_DGRAM */ locaddr.sin_family = AF_INET; locaddr.sin_port = htons(0); /* let the system choose */ locaddr.sin_addr.s_addr = htonl(inaddr_any); /* any addr of machine */ memset(&locaddr, \0, 8); bind(mysock, (struct sockaddr*)&locaddr, sizeof(locaddr)); 11 Binding of the socket (to an address) Read-back auto values When calling with sin_port=0, the value chosen by the system is not saved in the structure. We must use getsockname() to update the structure. Example: int length; getsockname(mysock, (struct sockaddr*)&locaddr, &length); /* note: length is an output argument; we have to pass its address */ /* the port number is now updated and can be displayed: but remember to convert to host byte order! */ printf( Port chosen by the system is %d, ntohs(locaddr.sin_port)); 12
Stream-based communication Walk-through example: TCP Echo Server 13 Echo : Create socket /* create socket for incoming connections */ int = socket(pf_inet, SOCK_STREAM, 0); 14
Echo : Bind local address struct sockaddr_in srvaddr; srvaddr.sin_family = AF_INET; /* IPv4 address */ srvaddr.sin_port = htons(8080); /* local port */ srvaddr.sin_addr.s_addr = htonl(inaddr_any); /* any interf. */ memset(&srvaddr.sin_zero, \0, 8); /* padding */ bind(, (struct sockaddr*)&srvaddr, sizeof(srvaddr)); 15 Echo : Listen to incoming connections /* listen for incoming connections */ listen(, MAXPENDING); 16
Echo : Accept incoming connections for (;;) { struct sockaddr_in cliaddr; /* address */ int cliaddrlen = sizeof(cliaddr); int ; /* socket for comm. to */ = accept(, (struct sockaddr*)&cliaddr, &cliaddrlen); 17 Echo Server: Accept incoming connections Server is now blocked, waiting for an incoming connection: 18
Echo : Create socket /* create TCP socket for outgoing connection */ int = socket(pf_inet, SOCK_STREAM, 0); 19 Echo Client: Bind local address (optional) struct sockaddr_in cliaddr; cliaddr.sin_family = AF_INET; /* IPv4 address */ cliaddr.sin_port = 0; /* any port is fine */ cliaddr.sin_addr.s_addr = htonl(inaddr_any); /* any interf. */ memset(&srvaddr.sin_zero, \0, 8); /* padding */ bind(, (struct sockaddr*)&cliaddr, sizeof(cliaddr)); 20
Echo Client: Connect to struct sockaddr_in srvaddr; srvaddr.sin_family = AF_INET; /* IPv4 address */ srvaddr.sin_port = htons(8080); /* port */ srvaddr.sin_addr.s_addr = inetaddr( 1.2.3.4 ); /* IP */ memset(&srvaddr.sin_zero, \0, 8); /* padding */ connect(, (struct sockaddr*)&srvaddr, sizeof(srvaddr)); 21 Echo Server: Accept incoming connections = accept(, (struct sockaddr*)&cliaddr, &cliaddrlen); Blocking call returns is the socket descriptor for communication with the 22
Echo Client: Send a message and wait for reply char msg[] = This is a test. ; send(, msg, strlen(msg), 0); /* wait for reply by calling recv() (not shown here) */ 23 Echo Server: Receive and send same messages #define BUFSIZE 32 char buf[bufsize]; int bytesrecv; bytesrecv = recv(, buf, BUFSIZE, 0); while (bytesrecv > 0) /* zero indicates end of transmission */ { send(, buf, bytesrecv, 0); /* echo */ bytesrecv = recv(, buf, BUFSIZE, 0); } 24
Echo Server: Close connection (and accept next) for (;;) {... = accept(, (struct sockaddr*)&cliaddr, &cliaddrlen);... } close(); 25 TCP Socket Features The must know the s address/port. The must only know its own port! Streaming socket: Transmission/Reception of a byte stream No correlation between bytes and segments (packets). Segmentation of the byte stream is out of the user s control (due to TCP s Flow Control) send( This is a test ); recv( Hello world ); recv() -> This i ; recv() -> s a te ; recv() -> st ; send() -> Hello send() -> world ; 26
TCP Socket Features is used to terminate a connection analogous to EOF (end-of-file) send(...); ; while (recv(...) > 0) { send(...) } ; Caution: Does not work with datagram socket e.g. UDP not connection-oriented = no close-notification 27 Blocking calls Be careful: calls are blocking! A call to recv(), send(), recvfrom(), and sendto() is by default a blocking call. Example: recvfrom(...); /* call blocks until a message is received */ sendto(...); The program will not be able to send if it does not receive anything! Solution 1: use the select() function Solution 2: multi-threaded applications 28
The select() function Used to monitor a set of descriptors (e.g. sockets, files, pipes, stdin). select() blocks until an event on the observed descriptors occurs: If we check a set in order to trigger read operations, select() returns the set that has data ready to be read. If select() is used in a loop, the initial set of descriptors must then always be initialized properly before re-calling select() The blocking behavior of select() can be changed by specifying a timeout value (last function argument). 29 Using the select() function Example: a wants to check two sockets for data to read /* the two socket descriptors are sock1 and sock2; let sockmax = (sock1 > sock2? sock1 : sock2) */ /* we first need a set of file descriptors */ fd_set readfds, backup; /* we initialize the set and add sock1 and sock2 to it */ FD_ZERO(&readfds); FD_SET(sock1, &readfds); FD_SET(sock2, &readfds); backup = readfds; /* used to save the original state */ for (;;) { select(sockmax, &readfds, NULL, NULL, NULL); if (FD_ISSET(sock1, &readfds)) recv(sock1, buf, MAX_LEN, 0); if (FD_ISSET(sock2, &readfds)) recv(sock2, buf, MAX_LEN, 0); readfds = backup; /* set readfds to initial state */ } 30
Multithreaded Spawn a new thread for each accepted connection for (;;) { = accept(, (struct sockaddr *)&cliaddr, &cliaddrlen); pid = fork(); if (pid == 0) /* child thread */ { close(); dostuff(); exit(0); } else /* parent thread */ { close(); /* continue in while loop: accept next connection... */ } } 31 Handle errors! Socket programming can easily lead to many errors. It is mandatory to check for errors! (for clarity, this is not done in these slides) Example: if (bind(mysock, (struct sockaddr*)&sockaddr, sizeof(sockaddr) < 0) { perror( Bind: ); /* prints out an explicit error message */ exit(-1); } Use perror() with all calls! (accept, connect,...) 32
Summary The socket API provides user-level abstractions for communication associations. One API for different communication types streaming, datagram-oriented, connection-oriented, connection-less and different protocols e.g. UDP, TCP, interprocess communiction, creates new socket descriptor destroys the descriptor configures/binds an address to a socket, to establish a connection-oriented session to send over pre-configured socket sendto()/recvfrom() to configure remote side on-the-fly 33