Introduction to Socket Programming April 2010 What is a socket? • An interface between application and network – The application creates a socket – The socket type dictates the style of communication • connection-oriented vs. connectionless • Once configured, the application can – pass data to the socket for network transmission – receive data from the socket (transmitted through the network by some other host) Two essential types of sockets • SOCK_STREAM – TCP sockets – reliable delivery – in-order guaranteed – connection-oriented We’ll look at this one App 3 2 1 • SOCK_DGRAM – UDP sockets – unreliable delivery – no order guarantees – no notion of “connection” D1 App socket Dest. 3 2 1 D2 socket D3 Client – high level view Create a socket Setup the server address Connect to the server Read/write data Shutdown connection Some utility functions • Byte Ordering: Host Byte Order to Network Byte Order: htons() , htonl() Network Byte Order to Host Byte Order: ntohs() , ntohl() • IP Address format: Ascii dotted to Binary: inet_aton() Binary to Ascii dotted: inet_ntoa() • Many others exist …… explore the man pages :D Socket() – A Connection Endpoint • This creates an endpoint for a network connection. int socket(int domain, int type, int protocol) domain = PF_INET (IPv4 communication) type = SOCK_STREAM (TCP) , SOCK_DGRAM (UDP) protocol = 0 (for our discussion) • Example : socket(PF_INET, SOCK_STREAM, 0); This will create a TCP socket. • The call returns a socket descriptor on success and 1 on an error. int connect_ socket(char *hostname, int port) { int sock; Ipv4 socket address structure struct socketaddr_in{ struct sockaddr_in sin; Hostent structure uint8_t struct hostent{ sin_len; /*length of the structure (16)*/ struct hostent *host; sa_falimily_t sin_family /*/*official AF_INT*/ charSTREAM, * h_name name of host*/ sock = socket(AF_ INET, SOCK_ 0); in_port_t sin_port /*/*16 bit TCP UDPof\ port number*/ char ** h_aliases; pointer ot or array if (sock == -1) struct in_addr sin_addr /* 32 bit Ipv4 address */ pointers to alias name*/ char sin_zero(8)/* unused*/ return sock; int h_addrtype /* host address type*/ } int h_length /* length of address */ family , int type, int protocol); host = gethostbyname(hostname);Socket(int char ** h_addr_list /*prt array of ptrs return nonnegative value fortoOK, -1 for errorwith \ if (host == NULL) { IPv4 or IPv6 address*/ close( sock); } return -1; struct hostent *gethostbyname( const char *hostname); unit16_t htons(unit16_t host16bitvaule) } /*Return nonnull pointer if OK, NULL error */ to /*Change the port number from host on byte order memset (& sin, 0, sizeof( sin)); network byte order */ connect(int socketfd, const struct sockaddr * servaddr, sin. sin_ family = AF_ INET; socket_t addrlen) /*Perform the TCP three way handshaking*/ sin. sin_ port = htons(port); sin. sin_ addr.s_ addr = *(unsigned long *) host -> h_ addr_ list[0]; if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { close (sock); return -1; } return sock; } Make the socket Resolve the host Setup the struct Connect Server – high level view Create a socket Bind the socket Listen for connections Accept new client connections Read/write to client connections Shutdown connection Bind() – Attaching to an IP and Port • A server process calls bind to attach itself to a specific port and IP address. int Bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) sockfd = socket descriptor returned by socket() my_addr = pointer to a valid sockaddr_in structure cast as a sockaddr * pointer addrlen = length of the sockaddr_in structure • Example : struct sockaddr_in my; my.sin_family = PF_INET; my.sin_port = htons(80); my.sin_addr.s_addr = INADDR_ANY; bzero(&my, 8) bind(sock, (struct sockaddr *)&my, sizeof(my)); Listen() – Wait for a connection • The server process calls listen to tell the kernel to initialize a wait queue of connections for this socket. Int Listen(int sock, int backlog) sock = socket returned by socket() backlog = Maximum length of the pending connections queue • Listen() returns -1 on error (otherwise 0). • Example: Listen(sock, 10); This will allow a maximum of 10 connections to be in pending state. Listening on a port (TCP) int make_ listen_ socket( int port) { struct sockaddr_ in sin; int sock; sock = socket( AF_ INET, SOCK_ STREAM, 0); if (sock < 0) Make the socket return -1; memset(& sin, 0, sizeof( sin)); sin. sin_ family = AF_ INET; Setup up the struct sin. sin_ addr. s_ addr = htonl( INADDR_ ANY); sin. sin_ port = htons( port); if (bind( sock, (struct sockaddr *) &sin, sizeof( sin)) < 0) Bind return -1; if(listen(sock,10) <bind(int 0) sockfd, const struct sockaddr * myaddr, socklen_t addrlen); return -1; /* return 0 if OK, -1 on error assigns a local protocol adress to a socket*/ return sock; } Accept() – A new connection ! • Accept is called by a Server process to accept new connections from new clients trying to connect to the server. Int Accept(int socket, (struct sockaddr *)&client, socklen_t *client_len) socket = the socket in listen state client = will hold the new client’s information client_len = pointer to size of the client structure • Example : struct sockaddr_in client; int len = sizeof(client); Accept(sock, (struct sockaddr *)&client, &len); Accept() return value accept() returns a new socket descriptor (small positive integer) or -1 on error. After accept returns a new socket descriptor, I/O can be done using the read() and write() system calls. read() and write() operate a little differently on sockets (vs. file operation)! Send / Recv – Finally Data !! • Send(), Recv() , Read() , Write() etc calls are used to send and receive data . Int send(int sock, void *mesg, size_t len, int flags) Int recv(int sock, void *mesg, size_t len, int flags) sock = A connected socket mesg = Pointer to a buffer to send/receive data from/in . len = Size of the message buffer flags = 0 (for our purpose) The return value is the number of bytes actually sent/received. • Example: char send_buffer[1024]; char recv_buffer[1024]; int sent_bytes; int recvd_bytes; sent_bytes = send(sock, send_buffer, 1024, 0); recvd_bytes = recv(sock, recv_buffer, 1024, 0); socket() bind() TCP Client Socket() connect() Connection establishment write() TCP Server Well-known port listen() accept() blocks until connection from client read() process request read() close() write() read() close() Dealing with blocking calls • Many functions block – accept(), connect(), recv() • For simple programs this is fine • What about complex connection routines – Multiple connections – Simultaneous sends and receives – Simultaneously doing non-networking processing Dealing with blocking (cont..) • Options – Create multi-process or multi-threaded code – Turn off blocking feature (fcntl() system call) – Use the select() function • What does select() do? – Input: a set of file descriptors – Output: info on the file-descriptors’ status – Therefore, can identify sockets that are “ready for use”: calls involving that socket will return immediately select function call • int status = select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *Timeout); – Status: # of ready objects, -1 if error – nfds: 1 +largest file descriptor to check – readfds: list of descriptors to check if read-ready – writefds: list of descriptors to check if write-ready – exceptfds: list of descriptors to check if an exception is registered – Timeout: time after which select returns Four parts of first project • 0: Get build , configure and run the minet stack • 1: HTTP Client • 2: Connection-at-a-time HTTP Server • 3: Simple select-based Multipleconnection-at-a-time server • 4: Complex …. ( Extra Credit )