前言
这篇文章好久之前就想写了 ,但因为种种原因耽搁了很久,博客已长草。本文主要介绍使用Java语言来实现类似于Apache的web服务器功能,但博主敬礼有限,只完成了主要部分的功能,像配置文件啥的那就靠自己继续完善了,下面我就抛砖引玉。
预备知识
写这么个小功能我们需要的预备知识便是http协议的基本知识,在http协议中,我们发送的请求数据称之为请求报文,服务器返回的数据称之为响应报文。我们需要了解这两种报文的基本格式。
具体的说明回头写文章详细介绍,详情可参考文章:http报文详解-简书
实现步骤
如果我们想使用Java实现的话,首先我们要弄清楚上面的请求报文和响应报文的格式。然后当用户对我们的服务器发送请求时,我们需要干的事请就是分析用户的求情报文,找到用户想要打开的网页进行展示,具体步骤如下:
这里解释为什么要建立新的线程,因为在同一时刻可能会有多个用户对服务器进行请求,多个线程可同时对多个用户进行响应,如果单线程可能用户需要排队请求。
Java代码实现
下面是Java实现web服务器功能(简版)的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
package kTWOServer; import java.io.* ; import java.net.* ; import java.util.* ; final class HttpRequest implements Runnable //多线程 { final static String CRLF = "\r\n"; Socket socket; // Constructor public HttpRequest(Socket socket) throws Exception { this.socket = socket; } // Implement the run() method of the Runnable interface. public void run() { try { processRequest(); } catch (Exception e) { System.out.println(e); } } @SuppressWarnings("deprecation") private void processRequest() throws Exception { System.out.println("远程主机地址:" + socket.getRemoteSocketAddress()); DataInputStream in = new DataInputStream(socket.getInputStream()); //请求 DataOutputStream os = new DataOutputStream(socket.getOutputStream()); //回复 String requestLine = in.readLine(); //读取第一行 System.out.println(requestLine); String headerLine = null; //循环读取报文段 while ((headerLine = in.readLine()).length() != 0) { System.out.println(headerLine); } // Extract the filename from the request line. StringTokenizer tokens = new StringTokenizer(requestLine); tokens.nextToken(); // 第一个为 "GET" String fileName = tokens.nextToken(); //第二个为对象路径 ,第三个为协议版本 fileName = "." + fileName; //相对路径 if(fileName.equals("./")) //设置默认打开的文件为index.html fileName +="index.html"; //查找文件 FileInputStream fis = null; boolean fileExists = true; try { fis = new FileInputStream(fileName); //试图取文件内容 } catch (FileNotFoundException e) { fileExists = false; //出错文件不存在 } String statusLine = null; String contentTypeLine = null; String entityBody = null; if (fileExists) { statusLine = "HTTP/1.1 200 OK" + CRLF; contentTypeLine = "Content-type: " + contentType( fileName ) + CRLF; } else { statusLine = "HTTP/1.1 404 Not Found" + CRLF; contentTypeLine = "Content-Type: text/html" + CRLF; entityBody = "<HTML>" + "<HEAD><TITLE>Not Found</TITLE></HEAD>" + "<BODY>Not Found</BODY></HTML>"; } // Send the status line. os.writeBytes(statusLine); // Send the content type line. os.writeBytes(contentTypeLine); // Send a blank line to indicate the end of the header lines. os.writeBytes(CRLF); // Send the entity body. if (fileExists) { sendBytes(fis, os); fis.close(); } else { os.writeBytes(entityBody); } in.close(); os.close(); socket.close(); } //返回消息到浏览器 private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception { // Construct a 1K buffer to hold bytes on their way to the socket. byte[] buffer = new byte[1024]; int bytes = 0; // Copy requested file into the socket's output stream. while((bytes = fis.read(buffer)) != -1 ) { os.write(buffer, 0, bytes); } } //文件类型判断 private static String contentType(String fileName) { if(fileName.endsWith(".htm") || fileName.endsWith(".html")) { return "text/html"; } if(fileName.endsWith(".jpg")) { return "image/jpg"; } if(fileName.endsWith(".txt")) { return "text/txt"; } return "application/octet-stream"; } } public class WebServer { @SuppressWarnings("resource") public static void main(String argv[]) throws Exception { int port = 8800; //指定端口 //定义socket ServerSocket serverSocket; serverSocket = new ServerSocket(port); //serverSocket.setSoTimeout(10000); //超时时间 //等待连接 while(true){ Socket server = serverSocket.accept(); //发现请求 HttpRequest t = new HttpRequest(server); //建立连接 t.run(); //启动新线程 } } } |
具体的代码意思可能对java有些基础的人都能看明白,大神无视。
eclipse工程下载:
结束语
说实话,博主的Java功底比较烂,因为博主不倾向于写Java项目,就连本文的Java项目都是因为学了计算机网络才写的一个实验。代码功能比较简单,只实现了最基本的响应功能,提供给初学者进行学习,如果发现问题还请反馈给我。