本章介绍Java Web服务器是如何运行的。从中可以知道Tomcat是如何工作的。
基于Java的Web服务器会使用java.net.Socket类和java.net.ServerSocket类这两个类,并通过发送HTTP消息进行通信。
因此,本章先介绍HTTP协议和这两个类。然后介绍一个简单的Web服务器。
1.1 HTTP协议
HTTP请求和相应信息
略。
1.2 Socket类
1.2.1 Socket类
Socket类表示一个客户端套接字(socket),即想要连接远程服务器应用程序时创建的套接字。
public Socket(String host, int port);//参数host是远程主机的名称或IP地址,参数port是连接远程应用程序的端口号。// 例如,想要通过80端口连接yahoo.com,可以使用以下语句创建Socket对象:new Socket("yahoo.com". 80);
一旦成功创建了Socket类的实例,就可以使用该实例发送或接收字节流。代码示例:
package demo;import java.io.*;import java.net.Socket;public class SocketDemo { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8080); OutputStream os = socket.getOutputStream(); boolean autoFlush = true; PrintWriter out = new PrintWriter(socket.getOutputStream(), autoFlush); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 发送一个HTTP请求到服务器 out.println("GET /index.jsp HTTP/1.1"); out.println("Host: localhost:8080"); out.println("Connection: Close"); out.println(); // 读取响应 boolean loop = true; StringBuffer sb = new StringBuffer(8096); while (loop) { if (in.ready()) { int i=0; while (i != -1) { i = in.read(); sb.append((char) i); } loop = false; } Thread.currentThread().sleep(50); } //展示响应信息 System.out.println(sb.toString()); socket.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }}
得到结果:
HTTP/1.1 200 Content-Type: text/html;charset=UTF-8Transfer-Encoding: chunkedDate: Thu, 26 Sep 2019 10:21:08 GMTConnection: close2000(以及Tomcat的index.jsp页面代码)
1.2.2 ServerSocket类
ServerSocket类和Socket类并不相同。服务器套接字必须时刻待命,它不知道客户端应用程序会在什么时候发起连接。ServerSocket类的构造函数需要指明IP地址和服务器套接字侦听的端口号。
ServerSocket类的其中一个构造函数的签名如下:
public SeverSocket (int port, int backLog, InetAddress bindingAddress);
创建了ServerSocket实例后,可以使其等待传入的连接请求,该连接请求会通过服务器套接字侦听的端口上绑定地址传入。这些工作可以通过调用ServerSocket类的accept方法完成。只有当接收到连接请求后,该方法才会返回,其返回值是一个Socket实例。可以使用该Socket对象与客户端应用程序进行字节流的发送/接收。
1.3 应用程序
针对以上知识点,做一个简单的web服务器。HttpServer类相当于一个已经启动了的Tomcat,Request类模拟访问URL,Response类模拟返回消息。
package webDemo;import java.io.*;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;public class HttpServer { // WEB_ROOT静态资源存放目录 public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webRoot"; // 关闭 public static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // 是否接收到关闭指令 private boolean shutdown = false; public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } private void await() { ServerSocket serverSocket = null; int port = 8081; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } while (!shutdown) { Socket socket; InputStream input; FileOutputStream output; try { socket = serverSocket.accept(); input = socket.getInputStream(); output = (FileOutputStream) socket.getOutputStream(); // 创建请求的对象和参数 Request request = new Request(input); request.parse(); // 创建响应对象 Response response = new Response(output); response.setRequest(request); response.sendStaticResource(); // 关闭Socket socket.close(); // 如果URL是shutdown命令 shutdown = request.getUri().equals(SHUTDOWN_COMMAND); } catch (Exception e) { e.getStackTrace(); continue; } } }}
package webDemo;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; } /** * 从InputStream对象中读取整个字节流 */ public void parse() { StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048]; try { i = input.read(buffer); } catch (IOException e) { e.getStackTrace(); i = -1; } for (int j = 0; j < i; j++) { request.append((char) buffer[j]); } System.out.println("request == " + request.toString()); uri = parseUri(request.toString()); } /** * GET /index.html HTTP/1.1 * 该方法在请求行中搜索第一个和第二个空格,从而找出URI * @param requestString * @return */ private String parseUri(String requestString) { int index1; int index2 = 0; index1 = requestString.indexOf(' '); if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); } if (index2 > index1) { return requestString.substring(index1 + 1, index2); } return null; }}
package webDemo;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;import java.io.*;/* HTTP Response = Status-Line *( (general - header | response-header | entity-header) CRLF) CRLF [ message-body ] Status-Line - HTTP-Version SP Status-Code SP Reason-Phrase CRLF */public class Response { Request request; OutputStream output; public Response(OutputStream output) { this.output = output; } public void setRequest(Request request) { this.request = request; } /** * 发送静态资源 * @throws IOException */ public void sendStaticResource() throws IOException { try { File file = new File(HttpServer.WEB_ROOT, request.getUri());//目录+文件名 if (file.exists()) { BufferedReader reader = new BufferedReader(new FileReader(file)); StringBuffer sb = new StringBuffer(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\r\n"); } StringBuffer result = new StringBuffer(); result.append("HTTP/1.1 200 ok \r\n"); result.append("Content-Language:zh-CN \r\n"); // charset=UTF-8 解决中文乱码问题// result.append("Content-Type:text/html;image/gif;charset=UTF-8 \r\n"); result.append("multipart/x-mixed-replace; boundary=myboundary"); result.append("Content-Length:" + file.length() + "\r\n"); result.append("\r\n" + sb.toString()); output.write(result.toString().getBytes()); output.flush(); output.close(); } else { //如果没有文件 String 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) { System.out.println("----" + e.toString()); } } private void covertImag(String line) { BASE64Decoder decoder = new BASE64Decoder(); try { byte[] b = decoder.decodeBuffer(line); for (int i = 0; i < b.length; i++) { if (b[i] < 0) { b[i] += 256; } } output.write(b); output.flush(); output.close(); } catch (IOException e) { System.out.println(e.toString()); } }}
index.html页面的代码:
Welcome to BrainySoftware Welcome to BrainySoftware.
该html页面中包含一个图片,但是我的代码无法显示代码,暂时没有找到解决办法,留着坑以后填。如果有大神知道如何处理,非常感激能教我。