问题描述
Tomcat小谈(1)最近闲来无事,就想着研究一些东东,正好想到了上次华为的面试,问了俺一堆堆tomcat的原理性的问题,过程很平淡,结局很惨烈,被完虐,让华为的面试官在我面前秀了一下大神的风采,顿时敬仰之情有如滔滔江水。正好在看论坛的时候,看到了一本我觉的很好的书《HowTomcatWorks》,传说这本书是tomcat研发人员之一写的,本人表示不关心。但是我觉的这本书对tomcat的理解还是很深刻的,于是拿来研究研究。Tomcat最为javaWeb开发者最常用的服务器,我相信90%以上的从业者都用的相当熟练了,且作为开源的Web服务器,器源代码已经公开,但是由于其稍显庞大,当然对于我来讲,是相当庞大,看起来就有些力不从心了。但是从其工作原来将,我们可以仿照她写一个比较简单的,相信动手做了,你对其整个原理会有比较深刻的理解。于是,我在空间把自己的学习经历写下来,以鼓励自己坚持看下去。今天,先来看下一个简单的Web服务器的工作原理。现在的Web通讯是基于HTTP/1.1,我们编写的服务器,就是接受来自浏览器的http请求,最后返回结果给浏览器。先看看HTTP请求和相应的格式。http请求:GET/index.htmlHTTP/1.1Accept:text/html,application/xhtml+xml,*/*Accept-Language:zh-CNUser-Agent:Mozilla/5.0(compatible;MSIE9.0;WindowsNT6.1;WOW64;Trident/5.0)Accept-Encoding:gzip,deflateHost:127.0.0.1:8080Connection:Keep-Alive
http响应:HTTP/1.1200OKServer:Microsoft-IIS/4.0Date:Mon,5Jan200413:13:33GMTContent-Type:text/htmlLast-Modified:Mon,5Jan200413:13:12GMTContent-Length:112<html><head><title>HTTPResponseExample</title></head><body>WelcometoeastTomcat</body></html>知道了协议,下面动手来写一个简单的web服务器,我分三个类,一个服务器,一个处理Request请求,一个处理Response响应HTTP服务器类:importjava.net.Socket;importjava.net.ServerSocket;importjava.net.InetAddress;importjava.io.InputStream;importjava.io.OutputStream;importjava.io.IOException;importjava.io.File;/***SOCKET服务器**/publicclassHttpServer{/**声明webroot*System.getProperty("user.dir")获取项目跟路径*/publicstaticfinalStringWEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";//关闭服务命令privatestaticfinalStringSHUTDOWN_COMMAND="/SHUTDOWN";//关闭服务接收privatebooleanshutdown=false;publicvoidawait(){ServerSocketserverSocket=null;intport=8080;//声明服务器端口try{InetAddressip=InetAddress.getByName("127.0.0.1");//创建ServerSocketserverSocket=newServerSocket(port,10,//指定客户连接请求队列的长度ip);//通过主机名确定主机的IP地址System.out.println("服务器启动成功:IP"+ip+"端口:"+port);}catch(IOExceptione){e.printStackTrace();System.exit(1);//系统非正常退出}//循环等待客户端while(!shutdown){Socketsocket=null;InputStreaminput=null;OutputStreamoutput=null;try{socket=serverSocket.accept();//阻塞,等待连接input=socket.getInputStream();output=socket.getOutputStream();//创建Request对象,见本包下的Request类Requestrequest=newRequest(input);//向Request中传递inputrequest.parse();//解析URI//创建Response对象,见本包下的Response类Responseresponse=newResponse(output);//向Response中传递outputresponse.setRequest(request);//向Response中传递Requestresponse.sendStaticResource();//调用发送方法//关闭socket,程序进入下一个循环,再次到socket=serverSocket.accept();阻塞处等待连接socket.close();/*检查URL请求中是否有SHUTDOWN_COMMAND命令:如:http://localhost:8080/SHUTDOWN,如果有将变为shutdown为true,循环结束,服务停止*/shutdown=request.getUri().equals(SHUTDOWN_COMMAND);}catch(Exceptione){//e.printStackTrace();//连接发生异常时返回重新等待连接(可能为客户端长时间未操作,连接异常中断等原因,request.getUri()获取null值,无需进行判断,直接重新循环)continue;}}}publicstaticvoidmain(String[]args){HttpServerserver=newHttpServer();server.await();}}Request类importjava.io.InputStream;importjava.io.IOException;/***本例中Request仅用来获取uri**/publicclassRequest{privateInputStreaminput;//定义InputStream存储服务器接收到的InputStreamprivateStringuri;//定义URI,并对外提供get方法publicRequest(InputStreaminput){this.input=input;//构造中初始化input为来自服务器的InputStream}//用来解析HTTP请求信息publicvoidparse(){//创建一个Stringbuffer存储input中的信息StringBufferrequest=newStringBuffer(2048);inti;//接收读取文件长度byte[]buffer=newbyte[2048];try{i=input.read(buffer);//读取input中的流}catch(IOExceptione){e.printStackTrace();i=-1;}for(intj=0;j<i;j++){request.append((char)buffer[j]);//将字节转为char追加在Stringbuffer后面}//获取到http请求,一般组成部分如下,这个可以在浏览器开发模式中看到:/**POST/index.htmlHTTP/1.1Host:127.0.0.1:8080Connection:keep-aliveCache-Control:max-age=0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,;q=0.8User-Agent:Mozilla/5.0(WindowsNT6.1;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/34.0.1847.116Safari/537.36Accept-Encoding:gzip,deflate,sdchAccept-Language:zh-CN,zh;q=0.8*/System.out.println(request);uri=parseUri(request.toString());}/*获取URI,在HTTP协议的头部存在这样的结构:方法—统一资源标识符(URI)—协议/版本---如:POST/examples/default.jspHTTP/1.1*所以只要截取前两个空格之间的URL即可*/privateStringparseUri(StringrequestString){intindex1,index2;//存放两个空格的位置index1=requestString.indexOf('');//获取第一个空格的位置if(index1!=-1){index2=requestString.indexOf('',index1+1);//从index1+1位置开始搜索下一个空格的位置if(index2>index1)returnrequestString.substring(index1+1,index2);//返回两个空格间的字符串,即为URI}returnnull;}publicStringgetUri(){returnuri;}}Response类:importjava.io.OutputStream;importjava.io.IOException;importjava.io.FileInputStream;importjava.io.File;/***本类仅处理静态资源**/publicclassResponse{privateRequestrequest;//定义Request对象,用来传递一个Request对象给Response对象privateOutputStreamoutput;//定义OutputStream存储服务器接收到的OutputStreampublicResponse(OutputStreamoutput){this.output=output;//构造中初始化output为来自服务器的OutputStreams}publicvoidsetRequest(Requestrequest){this.request=request;//传递一个Request对象给Response对象}//用来发送一个静态资源publicvoidsendStaticResource()throwsIOException{intBUFFER_SIZE=1024;byte[]bytes=newbyte[BUFFER_SIZE];FileInputStreamfis=null;try{/*创建文件对象,其中HttpServer.WEB_ROOT获取了项目的跟路径+request.getUri()获取的静态资源如:/index.html*/Filefile=newFile(HttpServer.WEB_ROOT,request.getUri());//如果文件存在,向连接方写出文件,(SOCKET中服务器向客户端写出)if(file.exists()){fis=newFileInputStream(file);intch=fis.read(bytes,0,BUFFER_SIZE);//读取文件中一个BUFFERwhile(ch!=-1){output.write(bytes,0,ch);//写出读取到的BUFFERch=fis.read(bytes,0,BUFFER_SIZE);//读取文件中一个BUFFER}}else{//文件如果找不到StringerrorMessage="HTTP/1.1404FileNotFoundrn"+"Content-Type:text/htmlrn"+"Content-Length:23rn"+"rn"+"<h1>404-FileNotFound!</h1>";output.write(errorMessage.getBytes());//向客户端写出一个HTTP响应头,并提示文件找不到}}catch(Exceptione){//如果发生异常,打印次异常信息//output.write(e.toString().getBytes());e.printStackTrace();}finally{if(fis!=null)fis.close();}}}仿照tomcat,在程序的同级目录下建立webroot文件夹,放要访问的资源,上面的代码仅支纯静态html,如,我在webroot下放了一个index.html在浏览器地址栏输入:http://127.0.0.1:8080/index.html即可正常访问运行结果:图片
解决方案
解决方案二:
解决方案三:
解决方案四:
http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/003/monkey/34.gif