我们可以将Tomcat 简单理解成一个Web 服务器。这一节主要是讲解Web 服务器是如何工作的。
Web 服务器也可以看作是Http 服务器,主要是因为它是使用Http 来与客户端进行通信的。一个基于Java 的Web 服务器,最重要的两个类是java.net.Socket 和 java.net.ServerSocket , 并通过Http 消息进行通信。其中前者是Socket 客户端,后者是Socket 服务端。
1.Socket 类
套接字(Socket)指的就是网络连接的一个端点。可以从网络中读取和写入数据。
Java 中创建一个Socket 有很多构造方法。可以通过主机名/IP地址、端口创建一个Socket 对象
new Socket("127.0.0.1",80);
Socket 创建以后,就可以使用它来发送和接收数据。
// 1.创建一个Socket 客户端对象
Socket socket = new Socket("127.0.0.1", 80);
// 2.关闭Socket
socket.close();
为了从Web 服务器获取适当的响应,需要发送一个遵守Http 协议的 Http 请求。
2.ServerSocket 类
上面Socket 类表示的是客户端的套接字,在你需要远程连接到一个服务器的时候,你需要创建的套接字。但是如果,你是创建的服务器应用,那么显然这里的逻辑会有所不同,应为作为服务端,你需要监听客户端的行为。
这里我们采用ServerSocket 类,ServerSocket 会等待客户端发来的请求,一旦ServerSocket 获得一个连接请求,它会创建一个Socket 实例来与客户端进行通信。
创建ServerSocket 也有多个构造方法,构造方法中有一个重要的属性:backlog,表示服务端开始拒绝传入的请求前,可传入的连接请求的最大值。
new ServerSocket(80,3);
在实际工作中,可以通过ServerSocket 的accept 方法监听客户端是否有连接请求,当接收到连接的请求时,accept 方法会返回一个Socket 对象,用来与客户端进行交互。
ServerSocket serverSocket = new ServerSocket(80,10);
int count = 0;
while(true){System.out.println(count++);Socket socket = serverSocket.accept();System.out.println("New connection accepted "+socket.getInetAddress()+":"+socket.getPort());socket.close();
}
3.模拟简单的Web 服务器
三个核心的类:HttpServer、Request、Response。
下面的示例就是这三个核心类的具体代码。
其中主方法main 在HttpServer 中,它初始化了一个ServerSocket 然后一直await 等待监听到客户端的程序。监听到了之后,将输入作为Request ,然后输出Response ,整个过程一直这样监听者直到接收到一个SHUTDOWN 的指令,退出程序。
public class HttpServer {// 这里定义的目录是用来存放一些静态的html 文件或者txt 文件或者jpg 等静态文件public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";// 关闭命令private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";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);}// 循环等待请求的到来while (!shutdown) {Socket socket = null;InputStream input = null;OutputStream output = null;try {socket = serverSocket.accept();input = socket.getInputStream();output = socket.getOutputStream();// 创建请求对象然后解析Request request = new Request(input);request.parse();// 创建响应对象Response response = new Response(output);response.setRequest(request);response.sendStaticResource();// 关闭socketsocket.close();// 判断是否有SHUTDOWN 指令shutdown = request.getUri().equals(SHUTDOWN_COMMAND);} catch (Exception e) {e.printStackTrace();continue;}}}
}
一个HttpServer 相当于一个ServerSocket 服务端的套接字,等待Socket 客户端的请求。
再收到客户端的请求后(我们这里是浏览器访问 http://localhost:8080/index.html),创建Request对象,并返回Response对象。
public class Request {private InputStream input;private String uri;public Request(InputStream input) {this.input = input;}public void parse() {// 从Socket 中读取字符集合StringBuffer request = new StringBuffer(2048);int i;byte[] buffer = new byte[2048];try {i = input.read(buffer);} catch (IOException e) {e.printStackTrace();i = -1;}for (int j = 0; j index1)return requestString.substring(index1 + 1, index2);}return null;}public String getUri() {return uri;}}
目前这里的请求Request 对象主要是用来解析浏览器的请求uri 。
public class Response {private static final int BUFFER_SIZE = 1024;Request request;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 foundString errorMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n"+ "Content-Length: 23\r\n" + "\r\n" + "File Not Found
";output.write(errorMessage.getBytes());}} catch (Exception e) {// thrown if cannot instantiate a File objectSystem.out.println(e.toString());} finally {if (fis != null)fis.close();}}
}
Response 对象主要是用来向浏览器发送静态资源。我们这里可以发送一个静态文本,图片或者html 文件。
演示:
但是到目前为止,测试 在IE下能够成功,在Google 浏览器下不能成功加载。不知道是不是Google 浏览器做了什么限制。