深入理解计算机系统 服务器.docx
- 文档编号:30139210
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:11
- 大小:150.57KB
深入理解计算机系统 服务器.docx
《深入理解计算机系统 服务器.docx》由会员分享,可在线阅读,更多相关《深入理解计算机系统 服务器.docx(11页珍藏版)》请在冰豆网上搜索。
深入理解计算机系统服务器
本文是我学习<深入理解计算机系统>中网络编程部分的学习笔记。
1.Web基础
web客户端和服务器之间的交互使用的是一个基于文本的应用级协议HTTP(超文本传输协议)。
一个web客户端(即浏览器)打开一个到服务器的因特网连接,并且请求某些内容。
服务器响应所请求的内容,然后关闭连接。
浏览器读取这些内容,并把它显示在屏幕上。
对于web客户端和服务器而言,内容是与一个MIME类型相关的字节序列。
常见的MIME类型:
MIME类型
描述
text/html
HTML页面
text/plain
无格式文本
image/gif
GIF格式编码的二进制图像
image/jpeg
JPEG格式编码的二进制图像
web服务器以两种不同的方式向客服端提供内容:
(1)静态内容:
取一个磁盘文件,并将它的内容返回给客户端
(2)动态内容:
执行一个可执行文件,并将它的输出返回给客户端
统一资源定位符:
URL
:
80/index.html
表示因特网主机上一个称为index.html的HTML文件,它是由一个监听端口80的Web服务器所管理的。
HTTP默认端口号为80
可执行文件的URL可以在文件名后包括程序参数, “?
”字符分隔文件名和参数,而且每个参数都用“&”字符分隔开,如:
http:
//www.ics.cs.cmu.edu:
8000/cgi-bin/adder?
123&456
表示一个/cgi-bin/adder的可执行文件,带两个参数字符串为123和456
确定一个URL指向的是静态内容还是动态内容没有标准的规则,常见的方法就是把所有的可执行文件都放在cgi-bin目录中
2.HTTP
HTTP标准要求每个文本行都由一对回车和换行符来结束
(1)HTTP请求
一个HTTP请求:
一个请求行(requestline)后面跟随0个或多个请求报头(requestheader),再跟随一个空的文本行来终止报头
请求行:
HTTP支持许多方法,包括GET,POST,PUT,DELETE,OPTIONS,HEAD,TRACE。
URI是相应URL的后缀,包括文件名和可选参数
version字段表示该请求所遵循的HTTP版本
请求报头:
HTTP1.1中一个IP地址的服务器可以是多宿主主机,例如 可以存在于同一服务器上。
HTTP1.1中必须有host请求报头,如host:
:
80 如果没有这个host请求报头,每个主机名都只有唯一IP,IP地址很快将用尽。
(2)HTTP响应
一个HTTP响应:
一个响应行(responseline)后面跟随0个或多个响应报头(responseheader),再跟随一个空的文本行来终止报头,最后跟随一个响应主体(responsebody)
响应行:
statuscode是一个三位的正整数
状态代码
状态消息
描述
200
成功
处理请求无误
301
永久移动
内容移动到位置头中指明的主机上
400
错误请求
服务器不能理解请求
403
禁止
服务器无权访问所请求的文件
404
未发现
服务器不能找到所请求的文件
501
未实现
服务器不支持请求的方法
505
HTTP版本不支持
服务器不支持请求的版本
两个最重要的响应报头:
Content-Type告诉客户端响应主体中内容的MIME类型
Content-Length指示响应主体的字节大小
响应主体中包含着被请求的内容。
3.服务动态内容
(1)客户端如何将程序参数传递给服务器
GET请求的参数在URI中传递,“?
”字符分隔了文件名和参数,每个参数都用一个"&"分隔开,参数中不允许有空格,必须用字符串“%20”来表示
HTTPPOST请求的参数是在请求主体中而不是URI中传递的
(2)服务器如何将参数传递给子进程
GET/cgi-bin/adder?
123&456HTTP/1.1
它调用fork来创建一个子进程,并调用execve在子进程的上下文中执行/cgi-bin/adder程序
在调用execve之前,子进程将CGI环境变量 QUERY_STRING 设置为"123&456",adder程序在运行时可以用unixgetenv函数来引用它
(3)服务器如何将其他信息传递给子进程
环境变量
描述
QUERY_STRING
程序参数
SERVER_PORT
父进程侦听的端口
REQUEST_METHOD
GET或POST
REMOTE_HOST
客户端的域名
REMOTE_ADDR
客户端的点分十进制IP地址
CONTENT_TYPE
只对POST而言,请求体的MIME类型
CONTENT_LENGTH
只对POST而言,请求体的字节大小
(4)子进程将它的输出发送到哪里
一个CGI程序将它的动态内容发送到标准输出,在子进程加载并运行CGI程序之前,它使用UNIXdup2函数将它标准输出重定向到和客户端相关连的已连接描述符
因此,任何CGI程序写到标准输出的东西都会直接到达客户端
4.综合:
Tinyweb服务器
(1)main程序
Tiny是一个迭代服务器,监听在命令行中传递来的端口上的连接请求,在通过调用open_listenfd函数打开一个监听套接字以后,执行无限服务器循环,不断接受连接请求(第16行),执行事务(第17行),并关闭连接它的那一端(第18行)
1intmain(intargc,char**argv)2{3intlistenfd,connfd,port,clientlen;4structsockaddr_inclientaddr;56/*Checkcommandlineargs*/7if(argc!
=2){8fprintf(stderr,"usage:
%s
(1);10}11port=atoi(argv[1]);1213listenfd=Open_listenfd(port);14while
(1){15clientlen=sizeof(clientaddr);16connfd=Accept(listenfd,(SA*)&clientaddr,&clientlen);//line:
netp:
tiny:
accept17doit(connfd);//line:
netp:
tiny:
doit18Close(connfd);//line:
netp:
tiny:
close19}20}
(2)doit函数
doit函数处理一个HTTP事物,首先读和解析请求行(requestline)(第11-12行),注意,我们使用rio_readlineb函数读取请求行。
Tiny只支持GET方法,如果客户端请求其他方法,发送一个错误信息。
然后将URI解析为一个文件名和一个可能为空的CGI参数字符串,并且设置一个标志表明请求的是静态内容还是动态内容(第21行)
如果请求的是静态内容,就验证是否为普通文件,有读权限(第29行)
如果请求的是动态内容,就验证是否为可执行文件(第37行),如果是,就提供动态内容(第42行)
1voiddoit(intfd)2{3intis_static;4structstatsbuf;5charbuf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];6charfilename[MAXLINE],cgiargs[MAXLINE];7rio_trio;89/*Readrequestlineandheaders*/10Rio_readinitb(&rio,fd);11Rio_readlineb(&rio,buf,MAXLINE);//line:
netp:
doit:
readrequest12sscanf(buf,"%s%s%s",method,uri,version);//line:
netp:
doit:
parserequest13if(strcasecmp(method,"GET")){//line:
netp:
doit:
beginrequesterr14clienterror(fd,method,"501","NotImplemented",15"Tinydoesnotimplementthismethod");16return;17}//line:
netp:
doit:
endrequesterr18read_requesthdrs(&rio);//line:
netp:
doit:
readrequesthdrs1920/*ParseURIfromGETrequest*/21is_static=parse_uri(uri,filename,cgiargs);//line:
netp:
doit:
staticcheck22if(stat(filename,&sbuf)<0){//line:
netp:
doit:
beginnotfound23clienterror(fd,filename,"404","Notfound",24"Tinycouldn'tfindthisfile");25return;26}//line:
netp:
doit:
endnotfound2728if(is_static){/*Servestaticcontent*/29if(!
(S_ISREG(sbuf.st_mode))||!
(S_IRUSR&sbuf.st_mode)){//line:
netp:
doit:
readable30clienterror(fd,filename,"403","Forbidden",31"Tinycouldn'treadthefile");32return;33}34serve_static(fd,filename,sbuf.st_size);//line:
netp:
doit:
servestatic35}36else{/*Servedynamiccontent*/37if(!
(S_ISREG(sbuf.st_mode))||!
(S_IXUSR&sbuf.st_mode)){//line:
netp:
doit:
executable38clienterror(fd,filename,"403","Forbidden",39"Tinycouldn'truntheCGIprogram");40return;41}42serve_dynamic(fd,filename,cgiargs);//line:
netp:
doit:
servedynamic43}44}
(3)clienterror函数
clienterror函数检查一些明显的错误,并把它报告给客户端
voidclienterror(intfd,char*cause,char*errnum,char*shortmsg,char*longmsg){charbuf[MAXLINE],body[MAXBUF];/*BuildtheHTTPresponsebody*/sprintf(body,"
%s\r\n",body,errnum,shortmsg);sprintf(body,"%s
%s:
%s\r\n",body,longmsg,cause);sprintf(body,"%s
TheTinyWebserver\r\n",body);/*PrinttheHTTPresponse*/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));}
(4)read_requesthdrs函数
Tiny不使用请求报头中的任何信息,仅仅调用 read_requesthdrs函数来读取并忽略这些报头。
注意,终止请求报头的空文本行是由回车和换行符组成的,在第6行中检查
1voidread_requesthdrs(rio_t*rp)2{3charbuf[MAXLINE];45Rio_readlineb(rp,buf,MAXLINE);6while(strcmp(buf,"\r\n")){//line:
netp:
readhdrs:
checkterm7Rio_readlineb(rp,buf,MAXLINE);8printf("%s",buf);9}10return;11}
(5)parse_uri函数
Tiny假设静态内容的主目录就是当前目录,可执行文件的主目录是./cgi-bin/ 任何包含字符串cgi-bin的URI都认为是对动态内容的请求。
首先将URI解析为一个文件名和一个可选的CGI参数字符串。
如果请求的是静态内容(第5行),就清除CGI参数串(第6行),然后将URI转换为一个相对的unix路径名,例如./index.html
如果URI是用'/'结尾的(第9行),我们就把默认的文件名加在后面(第10行)
如果请求的是动态内容(第13行),就会抽取所有的CGI参数(第14-20行),并将URI剩下的部分转换为一个相应的unix文件名(第21-22行)
1intparse_uri(char*uri,char*filename,char*cgiargs)2{3char*ptr;45if(!
strstr(uri,"cgi-bin")){/*Staticcontent*///line:
netp:
parseuri:
isstatic6strcpy(cgiargs,"");//line:
netp:
parseuri:
clearcgi7strcpy(filename,".");//line:
netp:
parseuri:
beginconvert18strcat(filename,uri);//line:
netp:
parseuri:
endconvert19if(uri[strlen(uri)-1]=='/')//line:
netp:
parseuri:
slashcheck10strcat(filename,"home.html");//line:
netp:
parseuri:
appenddefault11return1;12}13else{/*Dynamiccontent*///line:
netp:
parseuri:
isdynamic14ptr=index(uri,'?
');//line:
netp:
parseuri:
beginextract15if(ptr){16strcpy(cgiargs,ptr+1);17*ptr='\0';18}19else20strcpy(cgiargs,"");//line:
netp:
parseuri:
endextract21strcpy(filename,".");//line:
netp:
parseuri:
beginconvert222strcat(filename,uri);//line:
netp:
parseuri:
endconvert223return0;24}25}
(6)serve_static函数
Tiny提供四种不同的静态内容:
HTML文件、无格式的文本文件、GIF编码格式图片、JPEG编码格式图片
serve_static函数发送一个HTTP响应,其主体包含一个本地文件的内容。
首先我们通过检查文件名的后缀来判断文件类型(第7行),并且发送响应行和响应报头给客户端(第8-12行)。
注意用一个空行终止报头
第16行,我们使用 unixmmap函数将被请求文件映射到一个虚拟问存储器空间,调用mmap将文件srcfd的前filesize个字节映射到一个从地址srcp开始的私有只读虚拟存储器区域。
一旦文件映射到存储器,就不再需要它的描述符了,关闭这个文件(第17行)。
第18行执行的是到客户端的实际文件传动。
rio_writen函数拷贝从srcp位置开始的filesize个字节(已经被映射到了所请求的文件)到客户端的已连接描述符。
第19行释放了映射的虚拟存储器区域,避免潜在的存储器泄漏
1voidserve_static(intfd,char*filename,intfilesize)2{3intsrcfd;4char*srcp,filetype[MAXLINE],buf[MAXBUF];56/*Sendresponseheaderstoclient*/7get_filetype(filename,filetype);//line:
netp:
servestatic:
getfiletype8sprintf(buf,"HTTP/1.0200OK\r\n");//line:
netp:
servestatic:
beginserve9sprintf(buf,"%sServer:
TinyWebServer\r\n",buf);10sprintf(buf,"%sContent-length:
%d\r\n",buf,filesize);11sprintf(buf,"%sContent-type:
%s\r\n\r\n",buf,filetype);12Rio_writen(fd,buf,strlen(buf));//line:
netp:
servestatic:
endserve1314/*Sendresponsebodytoclient*/15srcfd=Open(filename,O_RDONLY,0);//line:
netp:
servestatic:
open16srcp=Mmap(0,filesize,PROT_READ,MAP_PRIVATE,srcfd,0);//line:
netp:
servestatic:
mmap17Close(srcfd);//line:
netp:
servestatic:
close18Rio_writen(fd,srcp,filesize);//line:
netp:
servestatic:
write19Munmap(srcp,filesize);//line:
netp:
servestatic:
munmap20}2122/*23*get_filetype-derivefiletypefromfilename24*/25voidget_filetype(char*filename,char*filetype)26{27if(strstr(filename,".html"))28strcpy(filetype,"text/html");29elseif(strstr(filename,".gif"))30strcpy(filetype,"image/gif");31elseif(strstr(filename,".jpg"))32strcpy(filetype,"image/jpeg");33else34strcpy(filetype,"text/plain");35}
(6)serve_dynamic函数
Tiny通过派生一个子进程并在子进程的上下文中运行一个CGI程序,来提供各种类型的动态内容。
serve_dynamic函数一开始就向客户端发送一个表明成功的响应行,,同时还包括带有信息的server报头。
第13行,子进程用来自请求URI的CGI参数初始化QUERY_STRING环境变量
第14行,子进程重定向它的标准输出到已连接文件描述符
第15行,加载并运行CGI程序,因为CGI程序运行在子进程的上下文中,它能够访问所有在调用execve函数之前就存在的打开文件和环境变量
第17行,父进程阻塞在对wait的调用中,等待子进程终止的时候,回收操作系统那个分配给子进程的资源
1voidserve_dynamic(intfd,char*filename,char*cgiargs)2{3charbuf[MAXLINE],*emptylist[]={NULL};45/*ReturnfirstpartofHTTPresponse*/6sprintf(buf,"HTTP/1.0200OK\r\n");7Rio_writen(fd,buf,strlen(buf));8sprintf(buf,"Server:
TinyWebServer\r\n");9Rio_writen(fd,buf,strlen(buf));1011if(Fork()==0){/*child*///line:
netp:
servedynamic:
fork12/*RealserverwouldsetallCGIvarshere*/13setenv
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入理解计算机系统 服务器 深入 理解 计算机系统
![提示](https://static.bdocx.com/images/bang_tan.gif)