CDS LAB 8 2007: SOCKET AND SELECT SOCKETS ---------------------------------------------------------------------------- Modify the server program so that uses fork() to create a new process that then issues an exec() call to execute the client program. Add the main() declaration: int status; After the listen() calls, insert: if (fork()==0) { // child process (becomes the client). Only safe to spawn child after the // listen() as otherwise the parent may not have set the socket up before // the child tries to connect! char port_buf[16]; // must write port number into a string for execl() sprintf(port_buf, "%d", ntohs(server.sin_port)); status = execl("./client", "client", port_buf, (char *) NULL); // runs ./client with argv[0] = "client", argv[1] = port_buf // The NULL is needed to specify that argc = 2. if (status != 0) perror("execl error:"); exit(-1); } Between the printf("Server finished.\n") and the return statement, insert: printf("Waiting for client...\n"); wait(&status); printf("... client finished\n"); return 0; ---------------------------------------------------------------------------- Now further extend the code so that the server spawns N client tasks (still via fork() and exec()). In this solution, we will give the clients an `id', and pass that value down to it. This client will add this id to the message, so that we can see who sent the message to the server. To the above modified server program, add the following code at the end of the main() declarations: int n, id; /* id is client's id (0<=id < n) */ if (argc < 2) { printf("Error no input argument\n"); exit(-1); } else { // convert the character string argument to an integer n = atoi(argv[1]); } Replace the listen() code with the following: if (listen(sock_1, n)) { perror("Server listen error"); exit(-1); } The client spawning code immediately below this is now modified as follows: for (id = 0; id < n; id++) { // create child if (fork()==0) { // child process: id'th client char port_buf[16], id_buf[16]; sprintf(port_buf, "%d", ntohs(server.sin_port)); sprintf(id_buf, "%d", id); status = execl("./client2", "client2", port_buf, id_buf, (char *) NULL); if (status != 0) perror("execl error:"); exit(-1); } } Immediately below this, enclose the accept(), recv(), send(), close() and wait() code in a loop: for (id=0; idh_addr, &server.sin_addr.s_addr, server_name->h_length); ---------------------------------------------------------------------------- SELECT: Rewrite this program using sockets. Need to add: #include #include #include From main(), the main difference is in the setup. After that, only the names of the file descriptors need changing. int main(int argc, char* argv[]) { int sock_1, sock_2[NUM_CHILD]; int maxfd; char buffer[MAXBUF]; fd_set fd_read_set; int i, counter; struct sockaddr_in client[NUM_CHILD], server; socklen_t namelen; /* ----Create TCP/IP socket---- */ sock_1 = socket(AF_INET, SOCK_STREAM, 0); if (sock_1 == -1) { perror("socket() Socket was not created"); exit(-1); } printf("Socket %d created successfully.\n", i); /* ----Address information for use with bind---- */ server.sin_family = AF_INET; /* it is an IP address */ server.sin_port = 0; /* use O/S defined port number */ server.sin_addr.s_addr = INADDR_ANY; /* use any interface on this host*/ /* ----Bind socket to address and port---- */ if (bind(sock_1, (struct sockaddr *) &server, sizeof(server))) { perror("Server bind error"); exit(-1); } /* ----Find out what port number was assigned---- */ namelen = sizeof(server); if (getsockname(sock_1, (struct sockaddr *) &server, &namelen)) { perror("Server get port number"); exit(-1); } printf("The assigned server port number %d is %d\n", i, ntohs(server.sin_port)); /* ----Set queue limits on socket---- */ if (listen(sock_1, NUM_CHILD)) { perror("Server listen error"); exit(-1); } /* ----Create the children ---- */ for (i=0; i < NUM_CHILD; i++) { /* ----Create child i ---- */ if (fork()==0) { /* ---- perform child i ---- */ int count; sock_1 = socket(AF_INET, SOCK_STREAM, 0); if (sock_1 < 0) { perror("Client socket creation"); exit(-1); } printf("Client %d socket created\n", i); /* ----Attempt to connect to the server; n.b. server struct set by parent if (connect(sock_1, (struct sockaddr *) &server, sizeof(server))) { perror("Client connection failure"); exit(-1); } sprintf(buffer, "Request from child %d", i); do { write(sock_1, buffer, MAXBUF); read(sock_1, &count , sizeof(count)); printf("Value of counter on child %d %d\n", i, count); fflush(stdout); sleep(i+1); } while (count > 0); printf("Child %d terminates\n", i); fflush(stdout); close(sock_1); exit(0); } } /* for (i...)*/ /* ----Now for the parent---- */ for (i=0; i < NUM_CHILD; i++) { namelen = sizeof(client[i]); sock_2[i] = accept(sock_1, (struct sockaddr *) &client[i], &namelen); if (sock_2[i] < 0) { perror("Server accept failed"); exit(-1); } printf("Server received connection from child %d %s\n", i, inet_ntoa(client[i].sin_addr)); } /* ----What is the maximum file descriptor for select---- */ maxfd = sock_2[i]; for (i=1; i < NUM_CHILD; i++) if (sock_2[i] > maxfd) maxfd = sock_2[i]; maxfd = maxfd + 1; counter = 0; /* ----Clear the read set---- */ FD_ZERO(&fd_read_set); while (counter < COUNT_MAX) { /* ----Set file descriptor set to NUM_CHILD read descriptors---- */ for (i=0; i < NUM_CHILD; i++) { FD_SET(sock_2[i], &fd_read_set); } /* ----Wait in select until file descriptors change---- */ select(maxfd, &fd_read_set, NULL, NULL, NULL); for (i=0; i < NUM_CHILD; i++) { /* ----Was it child i---- */ if (FD_ISSET(sock_2[i], &fd_read_set)) { read(sock_2[i], buffer, MAXBUF); printf("%s\n", buffer); counter++; write(sock_2[i], &counter, sizeof(counter)); } } } /* ----Terminate the children with waiting---- */ counter = -1; for (i=0; i < NUM_CHILD; i++) write(sock_2[i], &counter, sizeof(counter)); for (i=0; i < NUM_CHILD; i++) wait(NULL); exit(0); }