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

记得将图中标注的那个文件夹放入你的linux主机下的某个目录,并用root_dir指向它.

比如说,我将其放入了/home/lishushuakai/目录下,我的root_dir就被设置成了"/home/lishuhuakai/WebSiteSrc/html_book_20150808/reference".

好吧,现在可以运行代码了,enjoy it!

缺点

这只是很简单的一个服务器,各种各样的情况都没有考虑,你可以思考一下有那些极端的情况需要我们来考虑,我们将在接下来的一次次迭代中逐步解决这些问题.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注