Motivation EE 122: Sockets Kevin Lai September 11, 2002 Applications need Application Programming Interface (API) to use the network API: set of function types and data structures and constants Desirable characteristics - Standardized allows programmer to learn once, write anywhere - Flexible support multiple protocols - Simple to use - Complete allows program to use all functionality of a protocol protocols and APIs usually evolve in parallel laik@cs.berkeley.edu 2 Sockets Types of Sockets Berkeley sockets is the most popular network API - runs on Linux, FreeBSD, OS X, Windows - fed/fed off of popularity of TCP/IP Supports TCP/IP, UNIX interprocess communication Similar to UNIX file I/O API Based on C, single threaded model - does not require multiple threads Can build higher-level interfaces on top of sockets - e.g., Remote Procedure Call (RPC) Different types of sockets implements different service models - Stream v.s. datagram Stream socket - connection-oriented - reliable, in order delivery - e.g., ssh, http Datagram socket - connectionless - best-effort delivery, possibly lower delay - e.g., IP Telephony laik@cs.berkeley.edu 3 laik@cs.berkeley.edu 4 1
Chat Client Naming and Addressing create stream socket connect to server while still connected: - if user types data, send to server - if receive data from server, print IP Address - identifies a single host - 32 bits (not a number!) - written as dotted octets e.g., 0x0a000001 is 10.0.0.1 Host name - identifies a single host - variable length string - maps to one or more IP address e.g., www.berkeley.edu Port number - identifies an application on a host - 16 bit number laik@cs.berkeley.edu 5 laik@cs.berkeley.edu 6 Presentation Byte Ordering Solution Different CPU architectures have different byte ordering - why? Many errors for novice network programmers littleendian increasing memory addresses address A +1 address A high-order byte low-order byte 16-bit value uint16_t htons(uint16_t host16bitvalue); uint32_t htonl(uint32_t host32bitvalue); uint16_t ntohs(uint16_t net16bitvalue); uint32_t ntohs(uint32_t net32bitvalue); Use for all integers sent across network - including port numbers, but not IP addresses Floating point numbers - no widely used standard bigendian low-order byte high-order byte laik@cs.berkeley.edu 7 laik@cs.berkeley.edu 8 2
Initializing (1) Initializing (2) allocate socket int chat_sock = socket(af_inet, SOCK_STREAM, IPPROTO_TCP); int chat_sock; if ((chat_sock = socket(af_inet, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket"); printf("failed to create socket\n"); Why would socket() fail? Handling errors that occur rarely usually consumes most of systems code - exceptions (e.g., in java) helps this a somewhat laik@cs.berkeley.edu 9 laik@cs.berkeley.edu 10 Connecting (1) Connecting (2) struct sockaddr_in sin; struct hostent *host = gethostbyname (argv[1]); unsigned int server_addr = *(unsigned long *) host- >h_addr_list[0]; unsigned short server_port = atoi (argv[2]); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(server_addr); sin.sin_port = server_port; connect(chat_sock, (struct sockaddr *) &sin, sizeof(&sin)); struct sockaddr_in sin; struct hostent *host = gethostbyname (argv[1]); unsigned int server_addr = *(unsigned long *) host- >h_addr_list[0]; unsigned short server_port = atoi (argv[2]); memset (&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = server_addr; sin.sin_port = htons (server_port); if (connect(chat_sock, (struct sockaddr *) &sin, sizeof (sin)) < 0) { perror("connect"); printf("cannot connect to server\n"); abort(); laik@cs.berkeley.edu 11 laik@cs.berkeley.edu 12 3
Separating Data in a Stream Receiving Packets (1) Fixed length Record Fixed length int received; char buffer[record_len]; A B C 3 C 2 received = recv(chat_sock, buffer, RECORD_LEN, 0); process_packet(buffer, received); 0 1 2 3 4 5 6 7 8 9 Use s to partition Tradeoffs of two variable schemes? C 0 C 0 5 6 7 8 9 laik@cs.berkeley.edu 13 laik@cs.berkeley.edu 14 Receiving Packets (2) I/O Multiplexing (1) int receive_packets(char *buffer, int buffer_len, int *bytes_read) { int left = buffer_len - *bytes_read; received = recv(chat_sock, buffer + *bytes_read, left, 0); if (received < 0) { perror ("Read in read_client"); printf("recv in %s\n", FUNCTION ); if (received <= 0) { return close_connection(); *bytes_read += received; while (*bytes_read > RECORD_LEN) { process_packet(buffer, RECORD_LEN); *bytes_read -= RECORD_LEN; memmove(buffer, buffer + RECORD_LEN, *bytes_read); return 0; while (1) { if (receive_packets(buffer, buffer_len, &bytes_read)!= 0) { if (read_user(user_buffer, user_buffer_len, &user_bytes_read)!= 0) { laik@cs.berkeley.edu 15 laik@cs.berkeley.edu 16 4
I/O Multiplexing (2): Non-blocking I/O Multiplexing using select() int opts = fcntl (chat_sock, F_GETFL); if (opts < 0) { perror ("fcntl(f_getfl)"); opts = (opts O_NONBLOCK); if (fcntl (chat_sock, F_SETFL, opts) < 0) { perror ("fcntl(f_setfl)"); while (1) { if (receive_packets(buffer, buffer_len, &bytes_read)!= 0) { if (read_user(user_buffer, user_buffer_len, &user_bytes_read)!= 0) { select() - wait on multiple file descriptors/sockets and timeout - application does not consume CPU cycles while waiting - return when file descriptors/sockets are ready to be read or written or they have an error, or timeout exceeded advantages - simple - more efficient than polling disadvantages - does not scale to large number of file descriptors/sockets - more awkward to use than it needs to be laik@cs.berkeley.edu 17 laik@cs.berkeley.edu 18 I/O Multiplexing (3): select() Other I/O Models // already set descriptors non-blocking fd_set read_set; while (1) { Signal driven FD_ZERO (read_set); - application notified when I/O operation can be initiated FD_SET (stdin, read_set); FD_SET (chat_sock, read_set); - achieves similar CPU efficiency as select() select_retval = select(max(stdin, chat_sock) + 1, &read_set, NULL, NULL, &time_out); Asynchronous if (select_retval < 0) { - application notified when I/O operation is completed perror ("select"); - can achieve higher CPU efficiency than select()/signals on architectures that have DMA and available system bus if (select_retval > 0) { bandwidth if (FD_ISSET(chat_sock, read_set)) { if (receive_packets(buffer, buffer_len, &bytes_read)!= 0) { - mainly useful for very high bandwidth I/O Both add significant complexity relative to select() if (FD_ISSET(stdin, read_set)) { if (read_user(user_buffer, user_buffer_len, - must use locks to deal with being interrupted at arbitrary code &user_bytes_read)!= 0) { locations - sample complexity cost as threads laik@cs.berkeley.edu 19 laik@cs.berkeley.edu 20 5
Chat Server Summary create stream socket while 1: - if user connects, add to list of users - if receive data from client, send to all other clients Major sources of error for network programmers using sockets: - byte ordering - separating s in streams - using select() - misinterpreting the specification (not covered here) laik@cs.berkeley.edu 21 laik@cs.berkeley.edu 22 6