try { //新建server,监听本地8080端口 serverSocket=new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1")); System.out.println("....server start..."); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } while(!shutdown){ try { Socket socket=serverSocket.accept(); in=socket.getInputStream(); out=socket.getOutputStream(); Request request=new Request(in); //解析请求http请求 request.parse(); Response response=new Response(out); response.setRequest(request); response.sendStaticResource(); socket.close(); shutdown=request.getUri().equals(SHUTDOWN_COMMAND); }Request.java
public void parse(){ StringBuffer request=new StringBuffer(2048); int i; byte[] buffer=new byte[2048]; try { i=input.read(buffer); } catch (Exception e) { e.printStackTrace(); i=-1; } for (int j = 0; j < i; j++) { request.append((char)buffer[j]); } System.out.println(request.toString()); uri=parseUri(request.toString()); } public String getUri(){ return uri; } /** * http请求第一行格式 <p/> * 请求方法 uri 协议 * @param requestString * @return */ private String parseUri(String requestString){ int index1,index2; index1=requestString.indexOf(" "); if(index1 != -1){ index2 = requestString.indexOf(" ",index1+1); if(index2 > index1){ return requestString.substring(index1 + 1,index2); } } return null; }Response.java
public void sendStaticResource(){ byte[] bytes=new byte[BUFFER_SIZE]; FileInputStream fis=null; try { File file=new File(HttpServer.WEB_ROOT,request.getUri()); if(file.exists()){ fis=new FileInputStream(file); int ch=fis.read(bytes, 0, BUFFER_SIZE); while(ch!=-1){ out.write(bytes,0,ch); ch=fis.read(bytes, 0, BUFFER_SIZE); } }else{ String errorMsg="HTTP/1.1 404 File Not Found\r\n"+ "Content-Type: text/html\r\n"+ "Content-Length: 23\r\n"+ "\r\n"+ "<h1>File Not Found</h1>"; out.write(errorMsg.getBytes()); } } catch (Exception e) { System.out.println(e.toString()); }第二部分 在前章处理静态资源的基础上,加入处理servlet请求的方法。成为简单的servlet容器,而不仅仅是一个web服务器。
需要加入的内容:
1.request解析请求头uri后判断检查request是针对servlet还是静态资源
if(request.getUri().startsWith("/servlet/")){ ServletProcessor1 processor=new ServletProcessor1(); processor.process(request,response);2.实例化ServletProcessor类传入response,request。初始化一个类加载器并加载相关的serlvet类。
URL urls[]=new URL[1]; //根据不用协议流建立不同链接 ftp/http/ URLStreamHandler streamHandler=null; File classPath=new File(Constants.WEB_ROOT); String repository=(new URL("file",null,classPath.getCanonicalPath()+File.separator)).toString(); //指定第三个参数编译器才知道调用哪个构造函数,不然报错 urls[0]=new URL(null,repository,streamHandler); //如果ULR以/结尾则是目录,否则是jar,这里查找工作目录下的webroot System.out.println(repository); loader=new URLClassLoader(urls); ..... Class myClass=null; try { myClass=loader.loadClass(servletName); } catch (Exception e) { System.out.println(e.toString()); } Servlet servlet=null; try { servlet=(Servlet)myClass.newInstance(); servlet.service((ServletRequest)request, (ServletResponse)response); } catch (Exception e) { e.printStackTrace(); }
3.PrimitiveServlet类收到请求后调用service方法并打印输出结果。
public class PrimitiveServlet implements Servlet { ...... @Override public void service(ServletRequest arg0, ServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub System.out.println("from service"); PrintWriter out=response.getWriter(); out.println("Hello .ROses are red"); out.println("violets are blue"); out.flush(); } }4. Request和Response类分别需要实现ServletRequest,ServletResponse接口。这里我们先把需要实现的方法均留空不处理。需要处理的:
public class Response implements ServletResponse { ... public PrintWriter getWriter() throws IOException { // TODO Auto-generated method stub return new PrintWriter(out); } ... }5.加入门面模式Facade来区别tomcat系统开发人员和用户对request,reponse方法的使用提高安全性,拿出部分方法对使用者可见。大家可以看看很多源码都采用了Facade模式,比如代码生成器:rapid_framework,spring -quartz等。思考下HttpProcessor在每次请求时候都需要新建一个Processor来处理请求,可以利用对象池进行优化。每个Processor可以使用新的线程来处理,防止线程阻塞等。后面将说明这些问题。
/** * 定义外观类,所有方法在servlet中可见的,<p/> * 只是Request中的部分方法,其它方法不可访问,这样方法才更安全 * @author greenn [email protected] * CopyRight Huawei 2013-11-20 * */ public class HttpRequestFacade implements ServletRequest{ private HttpRequest request; public HttpRequestFacade(HttpRequest request) { // TODO Auto-generated constructor stub this.request=request; } @Override public AsyncContext getAsyncContext() { // TODO Auto-generated method stub return request.getAsyncContext(); } ....
try { servlet=(Servlet)myClass.newInstance(); servlet.service(new HttpRequestFacade(request), new HttpResponseFacade(response)); } catch (Exception e) { e.printStackTrace(); }第三部分 模拟Tomcat默认连接器,罗列一些优化性能的思路方法。 1.HttpConnector及HttpProcessor类均实现Runable接口来运行在自己的线程中无需等待某个HttpProcessor实例完成解析。
2.容器类均实现了LifeCycle接口。当创建一个HttpConnector实例后,就会调用其initialize()方法和start()方法。在组件的整个生命周期内,这两个方法均被调用一次。
3.HttpConnector维护HttpProcessor池。