自己动手写web服务器.doc
前几天开始看HOWTOMCATWORKS,因为有人推荐要研究TOMCAT源代码,看这本书是很有帮助的。看到第三章,这几天一直有事,也没心情看,现在想想,别管什么事抽点时间学习,学点是点。为了续得上思路,需要把原先的内容在搂一遍。从浏览器使用者的角度来看,我们都知道,打开浏览器,输入网址(URL),得到我们想看到的页面。任何一个WEB项目开发者都能够想象的出来,我们的浏览器和我们访问的网站所在的服务器发生了怎样的勾当。首先,浏览器会根据URL,REQUEST的请求,这个请求被服务器上的WEB服务器接受之后,然后返回HTML文本给浏览器,然后浏览器进行渲染显示。对于动态WEB服务器,还有一个功能就是把动态(如PHP、JSP、ASP)的语言进行解析,最后输出HTML文本。另外,网络基础稍微好一点的开发人员也会知道,每一个请求其实就是浏览器想服务器发送了一个HTTP的请求,请求格式有以下三部分组成请求方法URI协议/版本请求头REQUESTHEADER请求正文例如GET/INDEXHTMLHTTP/11HOSTLOCALHOST8080CONNECTIONKEEPALIVEACCEPTAPPLICATION/XML,APPLICATION/XHTMLXML,TEXT/HTMLQ09,TEXT/PLAINQ08,IMAGE/PNG,/Q05USERAGENTMOZILLA/50WINDOWSUWINDOWSNT61APPLEWEBKIT/53412KHTML,LIKEGECKOMAXTHON/30SAFARI/53412ACCEPTENCODINGGZIP,DEFLATEACCEPTLANGUAGEZHCNACCEPTCHARSETISO88591,,UTF8另外我们也知道,每一个HTTP请求,其实就是SOCKET的一次通信,SOCKET把请求数据发送到WEB服务器上。我们知道浏览器这边大概的活动流程了,那么在WEB服务器那边是怎么运作的呢它是怎么解析我们发过去的数据的它又是如何根据我们发送的请求,返回我们需要的资源的有了这些个疑问,要了解并实现一个WEB服务器就顺其自然了,最重要的是有了目的。根据HOWTOMCATWORKS第一章,我们实现一个简单的WEB服务器。大致思路如下1、首先我们应该监听指定端口,如80,或者8080或者其他。2、在该端口接受到消息之后开始处理。3、根据HTTP协议,我们可以知道在协议的第一行内容中包含了浏览器请求的资源名称以及路径。4、根据浏览器请求的资源,找到资源所在,然后通过SOCKET输出到浏览器。根据上面的思路,我们首先要有一个类来监听某一端口,这个类我们命名为HTTPSERVERJAVAIMPORTJAVANETSOCKETIMPORTJAVANETSERVERSOCKETIMPORTJAVANETINETADDRESSIMPORTJAVAIOSTREAMIMPORTJAVAIOOUTPUTSTREAMIMPORTJAVAIOIOEXCEPTIONIMPORTJAVAIOFILE/LISTENINGONTHEPORTAUTHORTHE5FIRELEARNTOMCAT/PUBLICCLASSHTTPSERVER{/WEB_ROOTISTHEDIRECTORYWHEREOURHTMLANDOTHERFILESRESIDEFORTHISPACKAGE,WEB_ROOTISTHE“WEBROOT“DIRECTORYUNDERTHEWORKINGDIRECTORYTHEWORKINGDIRECTORYISTHELOCATIONINTHEFILESYSTEMFROMWHERETHEJAVACOMMANDWASINVOKED/PUBLICSTATICFINALSTRINGWEB_ROOTSYSTEMGETPROPERTY“USERDIR“FILESEPARATOR“WEBROOT“//SHUTDOWNCOMMANDPRIVATESTATICFINALSTRINGSHUTDOWN_COMMAND“/SHUTDOWN“//THESHUTDOWNCOMMANDRECEIVEDPRIVATEBOOLEANSHUTDOWNFALSEPUBLICSTATICVOIDMAINSTRINGARGS{HTTPSERVERSERVERNEWHTTPSERVERSERVERAWAIT}PUBLICVOIDAWAIT{SYSTEMOUTPRINTLNWEB_ROOTSERVERSOCKETSERVERSOCKETNULLINTPORT8080TRY{SERVERSOCKETNEWSERVERSOCKETPORT,1,INETADDRESSGETBYNAME“127001“}CATCHIOEXCEPTIONE{EPRINTSTACKTRACESYSTEMEXIT1}//LOOPWAITINGFORAREQUESTWHILESHUTDOWN{SOCKETSOCKETNULLSTREAMNULLOUTPUTSTREAMOUTPUTNULLTRY{SOCKETSERVERSOCKETACCEPTSOCKETGETSTREAMOUTPUTSOCKETGETOUTPUTSTREAM//CREATEREQUESTOBJECTANDPARSEREQUESTREQUESTNEWREQUESTREQUESTPARSE//CREATERESPONSEOBJECTRESPONSERESPONSENEWRESPONSEOUTPUTRESPONSESETREQUESTREQUESTRESPONSESENDSTATICRESOURCE//CLOSETHESOCKETSOCKETCLOSE//CHECKIFTHEPREVIOUSURIISASHUTDOWNCOMMANDSHUTDOWNREQUESTGETURIEQUALSSHUTDOWN_COMMAND}CATCHEXCEPTIONE{EPRINTSTACKTRACECONTINUE}}}}另外需要一个REQUEST类来处理服务器的请求,以及一个RESPONSE来返回消息给客户端。REQUEST类的具体作用就是根据客户端发送过来的请求,然后根据消息的内容得到客户端请求的资源IMPORTJAVAIOSTREAMIMPORTJAVAIOIOEXCEPTIONPUBLICCLASSREQUEST{PRIVATESTREAMPRIVATESTRINGURIPUBLICREQUESTSTREAM{THIS}PUBLICVOIDPARSE{//READASETOFCHARACTERSFROMTHESOCKETSTRINGBUFFERREQUESTNEWSTRINGBUFFER2048INTIBYTEBUFFERNEWBYTE2048TRY{IREADBUFFER}CATCHIOEXCEPTIONE{EPRINTSTACKTRACEI1}FORINTJ0JINDEX1RETURNREQUESTSTRINGSUBSTRINGINDEX11,INDEX2}R