【问】Servlet如何工作?
问题:
一个Servlet容器为一个Servlet请求提供服务,基本要做三件事:
1、创建一个 request 对象并填充那些有可能被所引用的 servlet 使用的信息,如参数、头部、 cookies、查询字符串、 URI 等等。一个 request 对象是javax.servlet.ServletRequest 或 javax.servlet.http.ServletRequest 接口的一个实例。
2、创建一个 response 对象,所引用的 servlet 使用它来给客户端发送响应。一个 response对象avax.servlet.ServletResponse 或 javax.servlet.http.ServletResponse 接口的一个实例。
3、调用 servlet 的 service 方法,并传入 request 和 response 对象。在这里 servlet 会从 request 对象取值,给 response 写值。
2、创建一个 response 对象,所引用的 servlet 使用它来给客户端发送响应。一个 response对象avax.servlet.ServletResponse 或 javax.servlet.http.ServletResponse 接口的一个实例。
3、调用 servlet 的 service 方法,并传入 request 和 response 对象。在这里 servlet 会从 request 对象取值,给 response 写值。
Tomcat架构
由两个主要模块所组成的:连接器(connector)和容器(container)
Connector为接收到每一个 HTTP 请求构造一个 request 和 response 对象。然后它把流程传递给容器。容器从连接器接收到 requset 和 response 对象之后调用 servlet 的 service 方法用于响应。谨记,这个描述仅仅是冰山一角而已。这里容器做了相当多事情。例如,在它调用 servlet 的 service 方法之前,它必须加载这个 servlet,验证用户(假如需要的话),更新用户会话等等。一个容器为了处理这个进程使用了很多不同的模块,这也并不奇怪。例如,管理模块是用来处理用户会话,而加载器是用来加载 servlet 类等等。
一个简单Web服务器
一个基于 java 的 web 服务器使用两个重要的类: java.net.Socket 和 java.net.ServerSocket,并通过 HTTP 消息进行通信。
HTTP 请求、响应头
Socket、ServerSocket类
socket套接字是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。放在两个不同计算机上的两个应用可以通过连接发送和接受字节流。ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。
代码实现
GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Accept-Language: zh-Hans-CN,zh-Hans;q=0.8,en-US;q=0.5,en;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586
Accept-Encoding: gzip, deflate
Host: localhost:8001
Connection: Keep-Alive
|
HTTP/1.1 200 OK
Server: Microsoft-IIS/4.0 Date: Mon, 5 Jan 2004 13:13:33 GMT Content-Type: text/html Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT Content-Length: 112 <html> <head> <title>HTTP Response Example</title> </head> <body> Welcome to Brainy Software </body> </html> |
Socket、ServerSocket类
socket套接字是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。放在两个不同计算机上的两个应用可以通过连接发送和接受字节流。ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。
HttpServer.java(HTTP服务器)
package com.zzh;
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;
public class HttpServer
{
//服务器端口
private static final int port = 8001;
//Web根路经
public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
//退出标志
private boolean shutdown = false;
//退出命令
private static final String SHUT_DOWN = "/shutdown";
public static void main(String str[]) throws IOException
{
HttpServer server = new HttpServer();
server.start();
}
public void start() throws IOException
{
ServerSocket server = null ;
try {
server = new ServerSocket(port, 1, InetAddress.getByName("172.16.10.187"));
} catch (IOException e) {
e.printStackTrace();
}
while (!shutdown)
{
Socket socket = null;
try {
socket = server.accept();
System.out.println("socket accept");
// 输入流
InputStream inputStream = socket.getInputStream();
// 输出流
OutputStream outputStream = socket.getOutputStream();
//请求
Request request = new Request(inputStream);
request.parse();
//响应
Response response = new Response(outputStream);
response.setRequest(request);
response.sendStatisticResource();
System.out.println("socket close");
socket.close();
//关闭
shutdown = SHUT_DOWN.equals(request.getUri());
if(shutdown)
{
System.out.println("server close");
}
} catch (IOException e) {
if(socket!=null)
{
System.out.println("server exceptoin close");
socket.close();
}
e.printStackTrace();
}
}
}
}
|
Request.java(HTTP请求类)
package com.zzh;
import java.io.IOException;
import java.io.InputStream;
public class Request {
InputStream inputStream ;
String uri;
public Request(InputStream is)
{
inputStream = is;
}
//解析数据
public void parse()
{
int bufSize = 2048;
try
{
StringBuffer requestBuffer = new StringBuffer(bufSize);
byte[] buf = new byte[bufSize];
int rev = inputStream.read(buf);
for(int i=0 ; i < rev ; i++)
requestBuffer.append((char)buf[i]);
System.out.println(requestBuffer.toString());
ParseUri(requestBuffer.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
/*@
* 请求资源
*/
void ParseUri(String requestBuffer)
{
int index1 = requestBuffer.indexOf(' ');
if(index1 != -1)
{
int index2 = requestBuffer.indexOf(' ', index1+1);
if( index2 != -1 )
{
uri = requestBuffer.substring(index1+1, index2);
}
}
}
public String getUri()
{
return uri;
}
}
|
Response.java(HTTP响应类)
package com.zzh;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Response {
OutputStream outputStream;
Request request;
public Response(OutputStream outputStream)
{
this.outputStream = outputStream;
}
public void setRequest(Request req)
{
this.request = req;
}
//发送静态资源
public void sendStatisticResource() throws IOException
{
if( request.getUri() == null ) //chrome浏览器aceept两次
return ;
File file = new File(HttpServer.WEB_ROOT, request.getUri());
if(file.exists() && !request.getUri().equals("/"))
{
FileInputStream fis = null ;
try
{
int recvBufSize = 1000;
byte[] recvBuf = new byte[recvBufSize];
int cnt = 0;
//取资源、发送
fis = new FileInputStream(file);
cnt = fis.read(recvBuf, 0, recvBufSize);
while(cnt != -1)
{
outputStream.write(recvBuf);
cnt = fis.read(recvBuf, 0, recvBufSize);
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(fis!=null)
fis.close();
}
}
else
{
String responseString = "HTTP/1.1 404 Resource Not Find\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
try {
outputStream.write(responseString.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
|