- Servlet容器是如何工作的
1,创建一个request对象并填充那些有可能被所引用的servlet使用的信息,如参数、头部、 cookies、查询字符串、 URI 等等。一个 request 对象是javax.servlet.ServletRequest 或 javax.servlet.http.ServletRequest 接口的一个实例。
2,创建一个response对象,所引用的servlet使用它来给客户端发送响应。一个 response对象 javax.servlet.ServletResponse 或 javax.servlet.http.ServletResponse 接口的一个实例。
3,调用servlet的service方法,并传入 request 和 response 对象。在这里servlet会从 request 对象取值,给response写值。
- Catalina 架构
现在我自己模拟了一个简单的web服务器,如果下面这些代码很容易的理解了的话,那么对于web容器是如何工作的就基本没啥大问题了。我先简单的说下:web服务器就是在某一台虚拟主机上建立一个特定端口的服务器端socket,然后一直等待连接进来,一旦有连接连进来就new一个request和一个response,然后按照HTTP协议去解析request里面的请求参数,然后找到实际的资源文件,通过IO来读写,最后用response也按照HTTP协议去返回给客户端,就这样子就完成了一次请求和相应。下面的代码是入门级的一个web服务器,代码如下:
package linkin; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * @author LinkinPark * @Date 2015-3-11 * Http服务器。 */ public class HttpServer { /** * WEB_ROOT is the directory where our HTML and other files reside. For this * package, WEB_ROOT is the "webroot" directory under the working directory. * The working directory is the location in the file system from where the * java command was invoked. * */ public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; // shutdown command private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // the shutdown command received private boolean shutdown = false; public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } /** * 服务器的等待方法 */ public void await() { ServerSocket serverSocket = null; int port = 8080; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } // Loop waiting for a request while (!shutdown) { Socket socket = null; InputStream input = null; OutputStream output = null; try { //如果得到一个请求的话,就分别来建立一个输入流和输出流,然后分别建立一个请求和响应。 socket = serverSocket.accept(); input = socket.getInputStream(); output = socket.getOutputStream(); // create Request object and parse Request request = new Request(input); //控制台答应一下请求,然后解析出对应URL里面的请求路径 request.parse(); // create Response object Response response = new Response(output); response.setRequest(request); //返回响应,发送数据 response.sendStaticResource(); // Close the socket socket.close(); // check if the previous URI is a shutdown command //控制这个循环什么时候结束 shutdown = request.getUri().equals(SHUTDOWN_COMMAND); } catch (Exception e) { e.printStackTrace(); continue; } } } }
package linkin; import java.io.IOException; import java.io.InputStream; public class Request { private InputStream input; private String uri; public Request(InputStream input) { this.input = input; } public String getUri() { return uri; } //这里控制台打印一下请求的内容,方便我们查看 public void parse() { // Read a set of characters from the socket StringBuffer request = new StringBuffer(2048); byte[] buffer = new byte[2048]; int i; try { i = input.read(buffer); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j = 0; j < i; j++) { request.append((char) buffer[j]); } System.out.print(request.toString()); uri = parseUri(request.toString()); } // 发过来的请求里面第一行是信息头,类似如下:GET /linkin.html HTTP/1.1 private String parseUri(String requestString) { //http://localhost:8080/SHUTDOWN int index1, index2; index1 = requestString.indexOf(' '); if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); if (index2 > index1) //截取2个空格之间的内容,注意这里没有应用名了 return requestString.substring(index1 + 1, index2); } return null; } }
package linkin; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; /** * @author LinkinPark * @Date 2015-3-11 * */ public class Response { private static final int BUFFER_SIZE = 1024; Request request;//请求,主要是用请求URL里面的请求资源路径 OutputStream output;//输出流 public Response(OutputStream output) { this.output = output; } public void setRequest(Request request) { this.request = request; } //这里是需要一个方法,就是说发送响应回去 public void sendStaticResource() throws IOException { 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) { output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); } } else { // file not found String errorMessage = "HTTP/1.1 404 File Not Found " + "Content-Type: text/html " + "Content-Length: 23 " + " " + "<h1>File 11Not Found</h1>"; output.write(errorMessage.getBytes()); } } catch (Exception e) { // thrown if cannot instantiate a File object System.out.println(e.toString()); } finally { if (fis != null) fis.close(); } } }
ok,代码写完了,直接运行HttpServer的主方法启动服务器,现在要访问应用应该输入一个URL,前面代码也看到了,默认的目录就是在自己项目下面的webroot目录下,在这里我丢一个简单的HTML页面进去,用来访问:
<!DOCTYPE html> <html> <head> <title>linkin.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> </head> <body> 愿有人陪你一起颠沛流离。。。<br> </body> </html>输入URL:http://localhost:8080/linkin.html
输入URL:http://localhost:8080/SHUTDOWN 后台服务器停止了(因为服务器那段代码走完了,程序结束了)