CS360 Final Exam. Answer to Question 3
December 13, 1997
17 points
In the code below, I maintain a variable called nplayers,
which is the number of connections that I have made. When that is
less than 20, I call accept_connection() because I can service
more connections. When I get a connection, I call pipe()
before serving the connection. Then the parent closes the write end
of the pipe and the child closes the read end. Moreover, the parent
enters the read end of the pipe into the fdset called
pipes, and increments nplayers. When nplayers
hits 20, then I don't call accept_connection(). Instead, I
call select() on all the read ends of the pipes. The
connections that have terminated will return saying that there is
something to read on the pipe. If I performed that read, it would
tell me that the other end of the pipe is closed. However, I just
assume it's closed, so I close the file descriptor, remove it from
pipes, and decrement nplayers. This will happen to all
terminated connections, and I can go back to calling
accept_connection(). Neat, no?
#include "socketfun.h"
#define NPLAY 20
main()
{
int sock, fd, dummy;
int p[2];
int i;
int nplayers = 0;
fd_set pipes, work;
int maxfd = 0;
sock = serve_socket("games.cs.utk.edu", 7777);
FD_ZERO(&pipes);
while(1) {
if (nplayers == NPLAY) {
memcpy(&work, &pipes, sizeof(fd_set));
if (select(maxfd+1, &work, NULL, NULL, NULL) < 0) {
perror("select");
exit(1);
}
for (i = 0; i < maxfd+1; i++) {
if (FD_ISSET(i, &work)) {
close(i);
wait(&dummy);
FD_CLR(i, &pipes);
nplayers--;
}
}
} else {
fd = accept_connection(sock);
if (pipe(p) < 0) { perror("pipe"); exit(1); }
if (fork() == 0) {
close(p[0]);
dup2(fd, 0);
dup2(fd, 1);
close(fd);
close(sock);
for (i = 0; i <= maxfd; i++) if (FD_ISSET(i, &pipes)) close(i);
execlp("game", "game", NULL);
perror("execlp");
exit(1);
} else {
close(p[1]);
close(fd);
FD_SET(p[0], &pipes);
nplayers++;
if (maxfd < p[0]) maxfd = p[0];
}
}
}
}