/** * Determines the client's IP address, waits for a request, * and handles it. * * @param client_sock the client socket * @param client client socket information * @param client_length length of client structure * @param flag user-provided flags */ static void handle_client(int client_sock, struct sockaddr_storage * client, socklen_t client_length, struct flags * flag) { char client_ip[INET6_ADDRSTRLEN]; int determined_client_addr; /* determine client IP address */ determined_client_addr = 0; /* client IP is empty by default */ bzero(client_ip, sizeof(client_ip)); if (client->ss_family == AF_INET6) { if (inet_ntop(AF_INET6, &((struct sockaddr_in6*) client)->sin6_addr, client_ip, sizeof(client_ip)) == NULL) { perror("inet_ntop for client address"); } else { determined_client_addr = 1; } } else if (client->ss_family == AF_INET) { if (inet_ntop(AF_INET, &((struct sockaddr_in *) client)->sin_addr.s_addr, client_ip, sizeof(client_ip)) == NULL) { perror("inet_ntop for client address"); } else { determined_client_addr = 1; } } if (!determined_client_addr) { strncpy(client_ip, UNKNOWN_IP, sizeof(client_ip) - 1); } /* child process handles client and exits when done */ /* TODO only reads a line from client in this framework */ communicate_client(flag, client_sock, client_ip); (void) close(client_sock); }
/* * Function: handle_client() * * Descripton: * Accept a connection from a client, then fork to communicate the client * * Argument: * info_p: pointer to a server infomation * * Return value: * 0: success * other: fail */ int handle_client(struct server_info *info_p) { int ret = EXIT_SUCCESS; /* return value of this function */ int do_accept = 1; /* if non-zero, accept connection */ fd_set read_fds; /* list of file descriptor for reading */ int max_read_fd = 0; /* maximum number in the read fds */ info_p->current_connection = 0; FD_ZERO(&read_fds); FD_SET(info_p->listen_sd, &read_fds); max_read_fd = info_p->listen_sd; /* Catch SIGHUP */ handler.sa_handler = set_signal_flag; if (sigaction(SIGHUP, &handler, NULL) < 0) fatal_error("sigaction()"); /* Loop to wait a new connection */ for(;;) { if (do_accept) { int data_sd; /* socket descriptor for send/recv data */ socklen_t client_addr_len; /* length of `client_addr' */ struct sockaddr_storage client_addr; /* address of a client */ int select_ret; /* return value of select() */ fd_set active_fds; /* list of the active file descriptor */ struct timeval select_timeout; /* timeout for select() */ /* When catch SIGHUP, no more connection is acceptted. */ if (catch_sighup) { do_accept = 0; if (close(info_p->listen_sd)) fatal_error("close()"); continue; } /* Check a connection is requested */ active_fds = read_fds; select_timeout.tv_sec = 0; /* 0.5 sec */ select_timeout.tv_usec = 500000; select_ret = select(max_read_fd + 1, &active_fds, NULL, NULL, &select_timeout); if (select_ret < 0) { do_accept = 0; if (!catch_sighup) { perror("select()"); ret = EXIT_FAILURE; } if (close(info_p->listen_sd)) fatal_error("close()"); continue; } else if (select_ret == 0) { /* select() is timeout */ if (info_p->concurrent) delete_zombies(info_p); continue; } /* Accetpt a client connection */ if (FD_ISSET(info_p->listen_sd, &active_fds)) { client_addr_len = sizeof(struct sockaddr_storage); data_sd = accept(info_p->listen_sd, (struct sockaddr *)&client_addr, &client_addr_len); if (data_sd < 0) { do_accept = 0; if (!catch_sighup) { perror("accept()"); ret = EXIT_FAILURE; } if (close(info_p->listen_sd)) fatal_error("close()"); continue; } if (debug) fprintf(stderr, "called accept(). data_sd=%d\n", data_sd); /* Handle clients */ if (info_p->concurrent) { /* concurrent server. */ pid_t child_pid; child_pid = fork(); if (child_pid < 0) { /* fork() is failed. */ perror("fork()"); if (close(data_sd)) fatal_error("close()"); if (close(info_p->listen_sd)) fatal_error("close()"); do_accept = 0; continue; } else if (child_pid == 0) { /* case of a child */ int exit_value; if (close(info_p->listen_sd)) fatal_error("close()"); exit_value = communicate_client(info_p, data_sd); if (debug) fprintf(stderr, "child(%d) exits. value is %d\n", getpid(), exit_value); exit(exit_value); } else { /* case of the parent */ if (close(data_sd)) fatal_error("close()"); ++info_p->current_connection; if (info_p->max_connection < info_p->current_connection) { info_p->max_connection = info_p->current_connection; if (debug) fprintf (stderr, "The maximum connection is updated. The number is %zu.\n", info_p->max_connection); } delete_zombies(info_p); } } else { /* repeat server */ ret = communicate_client(info_p, data_sd); if (ret != EXIT_SUCCESS) if (close(info_p->listen_sd)) fatal_error("close()"); break; } } } else { /* case where new connection isn't accepted. */ if (info_p->concurrent) delete_zombies(info_p); if (info_p->current_connection == 0) break; } } return ret; }