summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2013-10-10 14:03:34 +0800
committerLAN-TW <lantw44@gmail.com>2013-10-10 14:03:34 +0800
commitaf7cf53111bb9784a04e16440ef1f003a29db8d3 (patch)
treeb456b6706c1e7344e22a7c2d23922aa864c9f92e
parent56ddfdf237610056f16dab036eecf912a1a0f540 (diff)
downloadsp2013-af7cf53111bb9784a04e16440ef1f003a29db8d3.tar.gz
sp2013-af7cf53111bb9784a04e16440ef1f003a29db8d3.tar.zst
sp2013-af7cf53111bb9784a04e16440ef1f003a29db8d3.zip
HW1: read_server 初版,仍有像是 SIGPIPE 之類的問題要處理
-rw-r--r--hw1/proc_r.c173
-rw-r--r--hw1/proc_w.c2
-rw-r--r--hw1/server.c55
-rw-r--r--hw1/server.h34
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.