一个简单Web Server 的实现
一个简单Web Server 的实现
下了 Ani-server 的源码, 代码不过几百行, 已经有一个基本 web server 的功能.参照其思路, 自己实现了下, 有点意思.
基本思路:
1)取得一 socket 的 fd --> bind --> listen -->select --> accept,
得到一 cli_fd, 通过它可以和请求服务的浏览器通信
2)从 cli_fd 读取数据, 判断是否是 http 的 GET, 获得被请求的文件, 将文件数据写入 cli_fd.
源码:
- /*
- * author: dengzhaoqun
- * date: 2011/07/21
- * platform: linux
- */
- #include <sys/select.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <stdlib.h>
- #include <string.h>
- #include <arpa/inet.h>
- #include <netinet/in.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #define DEFAULT_PAGE "index.html"
- #define WEB_ROOT "/home/xiaodeng/aniroot"
- void connection(int fd);
- int recv_new(int fd, char *str);
- void send_new(int fd, char *str);
- int get_file_size(int fd);
- int main()
- {
- int ser_fd, cli_fd, max_fd, fd;
- struct sockaddr_in ser_addr;
- struct sockaddr_in cli_addr;
- int ser_addr_len, cli_addr_len;
- fd_set readfds, master;
- int ret;
- ser_fd = socket(AF_INET, SOCK_STREAM, 0);
- if(ser_fd == -1)
- {
- perror("socket failed");
- exit(1);
- }
- ser_addr_len = sizeof(ser_addr);
- memset(&ser_addr, 0, ser_addr_len);
- ser_addr.sin_family = AF_INET;
- ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- ser_addr.sin_port = htons(80);
- ret = bind(ser_fd, (struct sockaddr *)&ser_addr, ser_addr_len);
- if(ret == -1)
- {
- perror("bind failed");
- exit(1);
- }
- listen(ser_fd, 5);
- FD_ZERO(&master);
- FD_SET(ser_fd, &master);
- max_fd = ser_fd;
- while(1)
- {
- readfds = master;
- ret = select(max_fd + 1, &readfds, 0, 0, 0);
- if(ret == -1)
- {
- perror("select failed");
- }
- else
- {
- for(fd = 0; fd < max_fd + 1; fd ++)
- {
- if(FD_ISSET(fd, &readfds))
- {
- if(fd == ser_fd)
- {
- cli_addr_len = sizeof(cli_addr);
- memset(&cli_addr, 0, cli_addr_len);
- cli_fd = accept(ser_fd,
- (struct sockaddr *)&cli_addr,
- (socklen_t *)&cli_addr_len);
- if(cli_fd > max_fd)
- {
- max_fd = cli_fd;
- }
- FD_SET(cli_fd, &master);
- }
- else
- {
- connection(fd);
- FD_CLR(fd, &master);
- }
- }
- }
- }
- }
- }/* main() */
- void
- connection(int fd)
- {
- char request[1000], resource[1000], *ptr;
- int ret;
- int page_fd;
- ret = recv_new(fd, request);
- ptr = strstr(request, " HTTP/");
- if(ptr == NULL)
- {
- perror("Not HTTP request");
- }
- else
- {
- *ptr = 0;
- ptr = NULL;
- if(strncmp(request, "GET ", 4) == 0)
- {
- ptr = request + 4;
- }
- if(strncmp(request, "HEAD ", 5) == 0)
- {
- ptr = request + 5;
- }
- if(ptr == NULL)
- {
- perror("Unknown Request. \n");
- }
- else
- {
- memset(resource, 0, sizeof(resource));
- strncpy(resource, WEB_ROOT, sizeof(resource));
- strncat(resource, ptr, sizeof(resource) - sizeof(WEB_ROOT) -1);
- if(ptr[strlen(ptr) -1] == '/' )
- {
- strncat(resource, DEFAULT_PAGE, sizeof(resource)
- - strlen(resource)
- - sizeof(DEFAULT_PAGE)
- - 1);
- }
- if((page_fd = open(resource, O_RDONLY)) == -1)
- {
- perror("open failed, file not found");
- send_new(fd, "HTTP/1.1 404 NOT FOUND\r\n");
- send_new(fd, "<html><head>404 NOT FOUND</head>");
- send_new(fd, "<body>Sorry, NOT FOUND</body></html>");
- close(fd);
- }
- else
- {
- int file_size;
- memset(request, 0, sizeof(request));
- file_size = get_file_size(page_fd);
- read(page_fd, request, file_size);
- send_new(fd, "HTTP/1.1 200 OK\r\n");
- send_new(fd, request);
- close(fd);
- }
- }
- }
- }/* connection() */
- void
- send_new(int fd, char *str)
- {
- int len;
- len = strlen(str);
- write(fd, str, len);
- }/* send_new() */
- int
- get_file_size(int fd)
- {
- struct stat buf;
- fstat(fd, &buf);
- return(buf.st_size);
- }/* get_file_size() */
- int
- recv_new(int fd, char *str)
- {
- char *p = str;
- int flag;
- flag = 0;
- while(1)
- {
- read(fd, p, 1);
- if(*p == '\r')
- flag = 1;
- if( flag && (*p == '\n'))
- {
- flag = 0;
- break;
- }
- p ++;
- }
- *(p-1) = '\0';
- return(strlen(str));
- }/* recv_new() */
编译后运行, 在浏览器中输入 "http://localhost/filename.html" , 即可在浏览器中显示 WEB_ROOT 目录下的相应 html 文件.
评论暂时关闭