@lishuhuakai
2016-11-04T13:18:57.000000Z
字数 4383
阅读 1916
GET /sample.jsp HTTP/1.1\r\nAccept: image/gif.image/jpeg,*/*\r\nAccept-Language: zh-cn\r\nConnection: Keep-Alive\r\nHost: localhost\r\nUser-Agent: Mozila/4.0(compatible;MSIE5.01;Window NT5.0)\r\nAccept-Encoding: gzip,deflate\r\n\r\n
HTTP/1.1 200 OK\r\nLast-Modified: Wed, 17 Oct 2007 03:01:41 GMT\r\nContent-Type: text/html\r\nContent-Length: 158\r\nDate: Wed, 17 Oct 2007 03:01:59 GMT\r\nServer: tiny-server/1.1\r\n\r\n
/* $begin forkwrapper */pid_t Fork(void){pid_t pid;if ((pid = fork()) < 0)unix_error("Fork error");return pid;}
int res = fork();if (res < 0) {exit(-1);}if (res == 0) {/* 子进程 */}else {/* 父进程 */}... /* 接下来做其他的处理 */
if (0 == Fork()) {/* 子进程 */}else {/* 父进程 */}... /* other code */
/** open_listenfd - open and return a listening socket on port* Returns -1 and sets errno on Unix error.*/int open_listenfd(int port){int listenfd, optval = 1;struct sockaddr_in serveraddr;/* 构建一个socket描述符 */if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)return -1;/* Eliminates "Address already in use" error from bind. */if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&optval, sizeof(int)) < 0) /* 设置端口复用 */return -1;/* Listenfd will be an endpoint for all requests to porton any IP address for this host */bzero((char *)&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons((unsigned short)port);if (bind(listenfd, (SA *)&serveraddr, sizeof(serveraddr)) < 0) /* 绑定 */return -1;/* Make it a listening socket ready to accept connection requests */if (listen(listenfd, LISTENQ) < 0) /* 监听 */return -1;return listenfd; /* 返回监听套接字 */}
void doit(int fd);
int is_static; /* 请求的是否为静态文件 */struct stat sbuf; /* 用于获得文件的信息 */char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];char filename[MAXLINE], cgiargs[MAXLINE];rio_t rio;/* Read request line and headers */Rio_readinitb(&rio, fd); /* rio首先要进行初始化才行 */Rio_readlineb(&rio, buf, MAXLINE); /* 读取一行数据 */
GET / HTTP/1.1\r\n
sscanf(buf, "%s %s %s", method, uri, version);if (strcasecmp(method, "GET")) {clienterror(fd, method, "501", "Not Implemented","Tiny does not implement this method");return;}read_requesthdrs(&rio);
is_static = parse_uri(uri, filename, cgiargs);if (stat(filename, &sbuf) < 0) {clienterror(fd, filename, "404", "Not found","Tiny couldn't find this file"); /* 没有找到文件 */return;}
if (is_static) { /* Serve static content */if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {clienterror(fd, filename, "403", "Forbidden","Tiny couldn't read the file"); /* 权限不够 */return;}serve_static(fd, filename, sbuf.st_size);}
void serve_static(int fd, char *filename, int filesize);// fd代表和客户端连接的socket描述符// filename文件所在路径// filesize文件大小
int srcfd;char *srcp, filetype[MAXLINE], buf[MAXBUF];/* Send response headers to client */get_filetype(filename, filetype);sprintf(buf, "HTTP/1.0 200 OK\r\n");sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);Rio_writen(fd, buf, strlen(buf)); /* 发送数据给客户端 */
srcfd = Open(filename, O_RDONLY, 0); /* 打开文件 */srcp = (char *)Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);Close(srcfd); /* 关闭文件 */Rio_writen(fd, srcp, filesize); /* 发送数据 */Munmap(srcp, filesize); /* 解除映射 */
void clienterror(int fd, char *cause, char *errnum,char *shortmsg, char *longmsg){char buf[MAXLINE], body[MAXBUF];/* Build the HTTP response body */sprintf(body, "<html><title>Tiny Error</title>");sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body);sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);/* Print the HTTP response */sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);Rio_writen(fd, buf, strlen(buf));sprintf(buf, "Content-type: text/html\r\n");Rio_writen(fd, buf, strlen(buf));sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));Rio_writen(fd, buf, strlen(buf));Rio_writen(fd, body, strlen(body));}
/* 网页的根目录 */const char * root_dir = "/home/lishuhuakai/WebSiteSrc/html_book_20150808/reference";/* / 所指代的网页 */const char * home_page = "index.html";/*-* 单进程版本的web server!当没有连接到来的时候,该进程会阻塞在Accept函数上,当然,这里的connfd也是阻塞版本的.* 也就是说,在对connfd执行write,read等函数的时候也会阻塞.这样一来,这个版本的server效率会非常低下.*/int main(int argc, char *argv[]){int listenfd = Open_listenfd(8080); /* 8080号端口监听 */while (true) /* 无限循环 */{struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);int connfd = Accept(listenfd, (SA*)&clientaddr, &len);doit(connfd);Close(connfd);}return 0;}
记得将图中标注的那个文件夹放入你的linux主机下的某个目录,并用root_dir指向它.
比如说,我将其放入了/home/lishushuakai/目录下,我的root_dir就被设置成了"/home/lishuhuakai/WebSiteSrc/html_book_20150808/reference".
好吧,现在可以运行代码了,enjoy it!
这只是很简单的一个服务器,各种各样的情况都没有考虑,你可以思考一下有那些极端的情况需要我们来考虑,我们将在接下来的一次次迭代中逐步解决这些问题.