diff options
author | LAN-TW <lantw44@gmail.com> | 2013-10-10 14:03:34 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2013-10-10 14:03:34 +0800 |
commit | af7cf53111bb9784a04e16440ef1f003a29db8d3 (patch) | |
tree | b456b6706c1e7344e22a7c2d23922aa864c9f92e | |
parent | 56ddfdf237610056f16dab036eecf912a1a0f540 (diff) | |
download | sp2013-af7cf53111bb9784a04e16440ef1f003a29db8d3.tar.gz sp2013-af7cf53111bb9784a04e16440ef1f003a29db8d3.tar.zst sp2013-af7cf53111bb9784a04e16440ef1f003a29db8d3.zip |
HW1: read_server 初版,仍有像是 SIGPIPE 之類的問題要處理
-rw-r--r-- | hw1/proc_r.c | 173 | ||||
-rw-r--r-- | hw1/proc_w.c | 2 | ||||
-rw-r--r-- | hw1/server.c | 55 | ||||
-rw-r--r-- | hw1/server.h | 34 |
4 files changed, 191 insertions, 73 deletions
diff --git a/hw1/proc_r.c b/hw1/proc_r.c index cdecb79..82b6822 100644 --- a/hw1/proc_r.c +++ b/hw1/proc_r.c @@ -8,68 +8,143 @@ #include <netinet/in.h> #include <stdio.h> #include <string.h> +#include <sys/select.h> #include <sys/socket.h> +#include <sys/time.h> #include <sys/types.h> #include <unistd.h> -bool procconn(server* svr, request* requestP, int maxfd){ - int ret; +bool procconn(server* svr, request* req, int maxfd){ + fd_set rset = svr->readfds; + fd_set wset = svr->writefds; + fd_set eset = svr->exceptfds; + struct timeval timeout = { 1, 0 }; + int rval; - struct sockaddr_in cliaddr; // used by accept() - int clilen; + if (select (maxfd, &rset, &wset, &eset, &timeout) < 0) { + perror ("select"); + return true; + } - int conn_fd; // fd for a new connection with client - int file_fd; // fd for file that we open for reading - char buf[512]; - int buf_len; + for (int i = 0; i < maxfd; i++) { + if (!FD_ISSET (i, &rset)) + continue; - // TODO: Add IO multiplexing - // Check new connection - clilen = sizeof(cliaddr); - conn_fd = accept(svr->listen_fd, (struct sockaddr*)&cliaddr, (socklen_t*)&clilen); - if (conn_fd < 0) { - if (errno == EINTR || errno == EAGAIN) - return true; // try again - if (errno == ENFILE) { - (void) fprintf(stderr, "out of file descriptor table ... (maxconn %d)\n", maxfd); - return true; + if (i == svr->listen_fd) { + struct sockaddr_in client_addr; + int client_len; + int conn_fd = accept (i, + (struct sockaddr*)&client_addr, (socklen_t*)&client_len); + if (conn_fd < 0) { + switch (errno) { + case EINTR: + case EAGAIN: + continue; + case EMFILE: + case ENFILE: + fprintf (stderr, "Too many open file ... " + "(maxconn %d)\n", maxfd); + continue; + default: + perror ("accept"); + continue; + } + } else { + req[conn_fd].conn_fd = conn_fd; + req[conn_fd].active = true; + strcpy(req[conn_fd].host, inet_ntoa(client_addr.sin_addr)); + fprintf(stderr, "Accept a new request: fd %d from %s\n", + conn_fd, req[conn_fd].host); + FD_SET (conn_fd, &(svr->readfds)); + } + } else { + rval = request_read(&req[i]); + if (rval <= 0) { + fprintf (stderr, "fd %d from %s: %s%s\n", + i, req[i].host, + rval < 0 ? "socket error: " : "command done", + rval < 0 ? strerror (errno) : ""); + request_free (&req[i], svr); + } else { + if (req[i].header_done) { + FD_CLR (req[i].conn_fd, &(svr->readfds)); + FD_SET (req[i].conn_fd, &(svr->writefds)); + } + } } - e_err_exit("accept"); } - requestP[conn_fd].conn_fd = conn_fd; - strcpy(requestP[conn_fd].host, inet_ntoa(cliaddr.sin_addr)); - fprintf(stderr, "getting a new request... fd %d from %s\n", conn_fd, requestP[conn_fd].host); - file_fd = -1; + for (int i = 0; i < maxfd; i++) { + if (!FD_ISSET (i, &wset)) + continue; + if (i == svr->listen_fd) + continue; - ret = request_read(&requestP[conn_fd]); - if (ret < 0) { - fprintf(stderr, "bad request from %s\n", requestP[conn_fd].host); - return true; - } - // requestP[conn_fd]->filename is guaranteed to be successfully set. - if (file_fd == -1) { - // open the file here. - fprintf(stderr, "Opening file [%s]\n", requestP[conn_fd].filename); - // TODO: Add lock - // TODO: check if the request should be rejected. - write(requestP[conn_fd].conn_fd, svr->accept_hdr, SVR_ACCEPT_HDR_LEN); - file_fd = open(requestP[conn_fd].filename, O_RDONLY, 0); - } + if (req[i].header_done) { + if (!req[i].header_accept) { + req[i].file_fd = open (req[i].filename, O_RDONLY); + fprintf (stderr, "fd %d from %s: open %s %s%s\n", + i, req[i].host, req[i].filename, + req[i].file_fd < 0 ? "error: " : "done", + req[i].file_fd < 0 ? strerror(errno) : ""); + if (req[i].file_fd < 0) { + write (req[i].conn_fd, svr->reject_hdr, SVR_REJECT_HDR_LEN); + request_free (&req[i], svr); + continue; + } - while (1) { - ret = read(file_fd, buf, sizeof(buf)); - if (ret < 0) { - fprintf(stderr, "Error when reading file %s\n", requestP[conn_fd].filename); - break; - } else if (ret == 0) break; - write(requestP[conn_fd].conn_fd, buf, ret); - } - fprintf(stderr, "Done reading file [%s]\n", requestP[conn_fd].filename); + struct flock lock_info = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0 + }; + rval = fcntl (req[i].file_fd, F_SETLK, &lock_info); + fprintf (stderr, "fd %d from %s: lock %s %s%s\n", + i, req[i].host, req[i].filename, + rval < 0 ? "error: " : "done", + rval < 0 ? strerror(errno) : ""); + if (rval < 0) { + write (req[i].conn_fd, svr->reject_hdr, SVR_REJECT_HDR_LEN); + request_free (&req[i], svr); + continue; + } + write (req[i].conn_fd, svr->accept_hdr, SVR_ACCEPT_HDR_LEN); + req[i].header_accept = true; + } else { + if (!req[i].buf_len) { + rval = read (req[i].file_fd, req[i].buf, SVR_BUFFER_SIZE); + if (rval < 0) { + if (errno != EINTR && + errno != EAGAIN) + { + // fatal error, close the connection + perror ("read"); + request_free (&req[i], svr); + } + continue; + } else if (rval == 0) { + fprintf (stderr, "%d done\n", req[i].conn_fd); + request_free (&req[i], svr); + continue; + } + + req[i].buf_set = 0; + req[i].buf_len = rval; + } - if (file_fd >= 0) close(file_fd); - close(requestP[conn_fd].conn_fd); - request_free(&requestP[conn_fd]); + rval = write (req[i].conn_fd, req[i].buf + req[i].buf_set, + req[i].buf_len); + if (rval < 0) { + perror("write"); + request_free (&req[i], svr); + continue; + } + req[i].buf_set += rval; + req[i].buf_len -= rval; + } + } + } return true; } diff --git a/hw1/proc_w.c b/hw1/proc_w.c index 7b13ed2..394a177 100644 --- a/hw1/proc_w.c +++ b/hw1/proc_w.c @@ -63,7 +63,7 @@ bool procconn(server* svr, request* requestP, int maxfd){ if (file_fd >= 0) close(file_fd); close(requestP[conn_fd].conn_fd); - request_free(&requestP[conn_fd]); + request_free(&requestP[conn_fd], NULL); return true; } diff --git a/hw1/server.c b/hw1/server.c index c503585..9c3178b 100644 --- a/hw1/server.c +++ b/hw1/server.c @@ -1,26 +1,29 @@ #include "server.h" #include "common.h" +#include <assert.h> #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> #include <netinet/in.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/select.h> #include <sys/socket.h> +#include <sys/time.h> #include <sys/types.h> #include <unistd.h> server svr; // server -// ====================================================================================================== // You don't need to know how the following codes are working void server_init(unsigned short port) { struct sockaddr_in servaddr; - int tmp; + int val, flags; gethostname(svr.hostname, sizeof(svr.hostname)); svr.port = port; @@ -33,8 +36,8 @@ void server_init(unsigned short port) { servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); - tmp = 1; - if (setsockopt(svr.listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&tmp, sizeof(tmp)) < 0) + val = 1; + if (setsockopt(svr.listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) e_err_exit("setsockopt"); if (bind(svr.listen_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) e_err_exit("bind"); @@ -43,50 +46,74 @@ void server_init(unsigned short port) { strcpy(svr.accept_hdr, "ACCEPT\n"); strcpy(svr.reject_hdr, "REJECT\n"); + + FD_ZERO(&svr.readfds); + FD_ZERO(&svr.writefds); + FD_ZERO(&svr.exceptfds); + FD_SET(svr.listen_fd, &svr.readfds); + + flags = fcntl(svr.listen_fd, F_GETFL); + if(flags < 0){ + perror("Warning: F_GETFL"); + return; + } + fcntl(svr.listen_fd, F_SETFL, flags | O_NONBLOCK); } void request_init(request* reqP) { reqP->conn_fd = -1; + reqP->buf_set = 0; reqP->buf_len = 0; reqP->filename = NULL; - reqP->header_done = 0; + reqP->file_fd = -1; + reqP->file_dev = 0; + reqP->file_ino = 0; + reqP->active = false; + reqP->header_done = false; + reqP->header_accept = false; } -void request_free(request* reqP) { +void request_free(request* reqP, server* svr) { if (reqP->filename != NULL) { free(reqP->filename); reqP->filename = NULL; } + close (reqP->conn_fd); + close (reqP->file_fd); + if (svr != NULL) { + FD_CLR (reqP->conn_fd, &(svr->readfds)); + FD_CLR (reqP->conn_fd, &(svr->writefds)); + FD_CLR (reqP->conn_fd, &(svr->exceptfds)); + } + request_init(reqP); } int request_read(request* reqP) { int r; - char buf[512]; + char buf[SVR_BUFFER_SIZE]; // Read in request from client r = read(reqP->conn_fd, buf, sizeof(buf)); if (r < 0) return -1; if (r == 0) return 0; - if (reqP->header_done == 0) { + if (!reqP->header_done) { char* p1 = strstr(buf, "\r\n"); int newline_len = 2; // be careful that in Windows, line ends with \r\n if (p1 == NULL) { - p1 = strstr(buf, "\n"); + p1 = strchr(buf, '\n'); newline_len = 1; - if (p1 == NULL) { - e_err_exit("this really should not happen..."); - } + assert (p1 != NULL); } size_t len = p1 - buf + 1; reqP->filename = (char*)e_malloc(len); - memmove(reqP->filename, buf, len); + memmove(reqP->filename, buf, len - 1); reqP->filename[len - 1] = '\0'; p1 += newline_len; reqP->buf_len = r - (p1 - buf); memmove(reqP->buf, p1, reqP->buf_len); - reqP->header_done = 1; + reqP->header_done = true; } else { reqP->buf_len = r; memmove(reqP->buf, buf, r); diff --git a/hw1/server.h b/hw1/server.h index 9cab01e..7720b5c 100644 --- a/hw1/server.h +++ b/hw1/server.h @@ -2,26 +2,42 @@ #define SP_HW1_SERVER_H #include <stdlib.h> +#include <sys/select.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> #define SVR_ACCEPT_HDR_LEN 8 #define SVR_REJECT_HDR_LEN 8 +#define SVR_HOSTNAME_MAX 512 +#define SVR_BUFFER_SIZE 512 + typedef struct { - char hostname[512]; // server's hostname + char hostname[SVR_HOSTNAME_MAX]; // server's hostname unsigned short port; // port to listen int listen_fd; // fd to wait for a new connection char accept_hdr[SVR_ACCEPT_HDR_LEN + 1]; char reject_hdr[SVR_REJECT_HDR_LEN + 1]; + fd_set readfds; + fd_set writefds; + fd_set exceptfds; } server; typedef struct { - char host[512]; // client's host - int conn_fd; // fd to talk with client - char buf[512]; // data sent by/to client - size_t buf_len; // bytes used by buf - // you don't need to change this. - char* filename; // filename set in header, end with '\0'. - int header_done; // used by handle_read to know if the header is read or not. + char host[SVR_HOSTNAME_MAX]; // client's host + int conn_fd; // fd to talk with client + char buf[SVR_BUFFER_SIZE]; // data sent by/to client + size_t buf_set; + size_t buf_len; // bytes used by buf + char* filename; // filename set in header, end with '\0'. + int file_fd; // fd to read or write local file + dev_t file_dev; + ino_t file_ino; + int active : 1; // whether this conn_fd is active + int header_done : 1; // used by request_read to know if the header is done + int header_accept : 1; // used by procconn to know if ACCEPT/REJECT is sent } request; extern server svr; // server @@ -33,7 +49,7 @@ void server_init(unsigned short port); void request_init(request* reqP); // free resources used by a request instance -void request_free(request* reqP); +void request_free(request* reqP, server* svr); // return 0: socket ended, request done. // return 1: success, message (without header) got this time is in reqP->buf with reqP->buf_len bytes. read more until got <= 0. |