Lecture 24 ● ● ● ● ● Log into Linux. Copy directory /home/hwang/cs375/lecture24 Final project posted. Due during finals week. Reminder: No class next Tuesday (11/24) Questions? Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 1 Outline ● Internet (IP) sockets ● Concurrent servers Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 2 Internet (IP) Sockets ● ● ● IP or Internet sockets can be used to communicate between two processes on two different machines connected by a network. (Or for IPC on a single machine.) An IP socket is an address/port-number pair and uniquely identifies an endpoint. A IP socket pair consists of both the server and client sockets. Every TCP connection is uniquely associated with a socket pair. Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 3 Internet (IP) Sockets Client 198.69.10.2 Server 206.62.226.35 Client 102.43.83.204 client1 server client1 198.69.10.2:1500 206.62.226.35:21 (*:21, *.*) 102.43.83.204:1501 206.62.226.35:21 client2 198.69.10.2:1501 206.62.226.35:21 Thursday, November 19 server (child1) server (child2) 206.62.226.35:21 198.69.10.2:1500 206.62.226.35:21 102.43.83.204:1501 server (child3) 206.62.226.35:21 198.69.10.2:1501 CS 375 UNIX System Programming - Lecture 24 4 Internet (IP) Sockets ● ● Just as for local sockets, the server will call socket( ), bind( ), listen( ), and accept( ). First call socket( ) to create the socket: sfd = socket(domain, type, protocol); ● The domain parameter is now PF_INET. type is usually either SOCK_DGRAM (UDP) or SOCK_STREAM (TCP). (We will use SOCK_STREAM.) The protocol should be 0. Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 5 Internet (IP) Sockets ● bind( ) is used to assign an address (TCP port) to the socket. Address family is now AF_INET. struct sockaddr_in in_addr; // network struct sockaddr *p_addr = (struct sockaddr *)(&in_addr); in_addr.sin_family = AF_INET; in_addr.sin_port = htons(2400); in_addr.sin_addr.s_addr = htonl(INADDR_ANY); int len = sizeof(struct sockaddr_in); bind(sfd, p_addr, len); Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 6 Internet (IP) Sockets ● ● ● On the server, we bind our socket to a particular IP address and port number. An IP address of INADDR_ANY will bind to all local IP addresses. We could instead bind to a single IP address on a single interface. Our server will bind to port 2400 in this example. Note the use of the htonl( ) and htons( ) routines. Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 7 Internet (IP) Sockets ● Instead of binding to a particular port, if we specify a port number of 0 in the address we will be assigned an available port number dynamically. The getsockname( ) routine can then be used to find the port number: getsockname(sfd, p_addr, &addrlen); cout << “Port #: ” << ntohs(in_addr.sin_port) << endl; ● Note the use of ntohs( ). Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 8 Internet (IP) Sockets ● The server then uses listen( ) and accept( ) just as for UNIX sockets. result = listen(sfd, qlength); nfd = accept(sfd, p_addr, &len); ● The p_addr parameter is a pointer to a sockaddr structure (that is really of type sockaddr_in). This will be filled with connecting client info (IP address and port number). Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 9 Internet (IP) Sockets ● On the client we need only socket( ) and connect( ). We use the server IP and port numbers in the address: cfd = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in in_address; struct sockaddr *address = (struct sockaddr *)(&in_address); in_address.sun_family = AF_INET; in_address.sin_port = htons(2400); in_address.sin_addr.s_addr = inet_addr("10.10.0.9"); // csserver int len = sizeof(struct sockaddr_in); res = connect(cfd, address, len); Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 10 Internet (IP) Sockets ● ● In this example inet_addr( ) was used to convert an IP number from dotted-quad to network byte order. Typically the IP number would be obtained via getaddrinfo( ). Since our socket file descriptor is not bound to a local endpoint, connect( ) will do this for us. This is usually what we want. You could use bind( ) to bind to a particular local port number before the call to connect( ). Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 11 Internet (IP) Sockets ● ● read( ) and write( ) can be used to send data via the socket. There also are recv( ) and send( ) routines that can used and often are recommended because they provide more options via their flags parameter. Consult the man pages, if you want to use these. Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 12 Examples ● ● network_server.cpp is an example server. It is similar to the local_server.cpp from last lecture but is modified to use a TCP/IP socket instead of a local (UNIX) socket. It illustrates dynamic port assignment and the use of getsockname( ). network_client.cpp is a similarly modified version of local_client.cpp. It requires two command-line arguments, an IP address (in dotted-quad notation) and a port number. Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 13 Concurrent Server ● Skeleton code for a concurrent server ... listenfd = socket( ... ); bind( listenfd, ... ); listen(listenfd, ... ); while ( true ) { connfd = accept( listenfd, ... ); if( (pid = fork()) == 0) { close( listenfd ); doit(connfd); // or exec(...) close( connfd ); // if not exec'd exit(0); } close( connfd ); // parent } Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 14 Concurrent Server ● It is important that the parent call close( ) for each connected socket returned by accept( ). This ensures that the parent does not run out of file descriptors, but more importantly it ensures that when the child closes the connected socket the client connection will be terminated. (This does not happen until the open reference count on the socket reaches zero.) Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 15 Avoiding Zombies ● ● As noted previously, the parent of an exiting process must issue a wait on the process for it to terminate completely. We do not want the main server process to have to issue these waits for the processes handling client requests. Typically, in order to avoid creating zombie processes, the doublefork technique covered earlier is used. Thursday, November 19 CS 375 UNIX System Programming - Lecture 24 16