@lishuhuakai
2016-11-04T21:18:57.000000Z
字数 4383
阅读 1663
GET /sample.jsp HTTP/1.1\r\n
Accept: image/gif.image/jpeg,*/*\r\n
Accept-Language: zh-cn\r\n
Connection: Keep-Alive\r\n
Host: localhost\r\nUser-Agent: Mozila/4.0(compatible;MSIE5.01;Window NT5.0)\r\n
Accept-Encoding: gzip,deflate\r\n
\r\n
HTTP/1.1 200 OK\r\n
Last-Modified: Wed, 17 Oct 2007 03:01:41 GMT\r\n
Content-Type: text/html\r\n
Content-Length: 158\r\n
Date: Wed, 17 Oct 2007 03:01:59 GMT\r\n
Server: 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 port
on 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!
这只是很简单的一个服务器,各种各样的情况都没有考虑,你可以思考一下有那些极端的情况需要我们来考虑,我们将在接下来的一次次迭代中逐步解决这些问题.