最近在學習HTTP協議,仍是但願動手去作一作,因此就本身實現了一個http服務器,主要功能是將http請求封裝httpRequest,經過解析web.xml,用不一樣的handler處理不一樣的uri,而後再將封裝好的httpResponse還原成http響應返回瀏覽器。html
代碼已經成功上傳至 GitHub java
若是對你學習JavaNIO有幫助的話,記得給個star哦!git
https://github.com/hansiming/HttpServerByJavaNIOgithub
代碼web
使用java nio實現監聽,完成服務器監聽線程apache
package com.cszjo.com.http.server; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; import org.apache.log4j.Logger; import com.cszjo.com.http.handler.HttpHandler; import com.cszjo.com.http.utils.XMLUtil; /** * @Title: Server.java * @Description: 打開服務 * @author: Han * @date: 2016年7月12日 下午7:22:47 */ public class Server implements Runnable { private boolean interrupted = false; private Logger logger = Logger.getLogger(Server.class); public Server(boolean interrupted) { this.interrupted = interrupted; } @Override public void run() { try { //打開一個選擇器 Selector selector = Selector.open(); //打開ServerSocketChannel通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //獲得ServerSocket對象 ServerSocket serverSocket = serverSocketChannel.socket(); //ServerSocketChannel通道監聽server.xml中設置的端口 String portStr = XMLUtil.getRootElement("server.xml").element("port").getText(); serverSocket.setReuseAddress(true); try { serverSocket.bind(new InetSocketAddress(Integer.parseInt(portStr))); } catch (Exception e) { logger.error("綁定端口失敗,請檢查server.xml中是否設置了port屬性"); return; } logger.info("成功綁定端口" + portStr); //將通道設置爲非阻塞模式 serverSocketChannel.configureBlocking(false); //將serverSocketChannel註冊給選擇器,並綁定ACCEPT事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); logger.info("服務器啓動成功"); while(!interrupted) { //查詢就緒的通道數量 int readyChannels = selector.select(); //沒有就緒的則繼續進行循環 if(readyChannels == 0) continue; //得到就緒的selectionKey的set集合 Set<SelectionKey> keys = selector.selectedKeys(); //得到set集合的迭代器 Iterator<SelectionKey> iterator = keys.iterator(); while(iterator.hasNext()) { SelectionKey key = iterator.next(); if(key.isAcceptable()) { //該key有ACCEPT事件 //將監聽獲得的channel強轉爲ServerSocketChannel ServerSocketChannel server = (ServerSocketChannel) key.channel(); //獲得接收到的SocketChannel SocketChannel socketChannel = server.accept(); if(socketChannel != null) { logger.info("收到了來自" + ((InetSocketAddress)socketChannel.getRemoteAddress()).getHostString() + "的請求"); //將socketChannel設置爲阻塞模式 socketChannel.configureBlocking(false); //將socketChannel註冊到選擇器 socketChannel.register(selector, SelectionKey.OP_READ); } } else if (key.isReadable()) { //該key有Read事件 SocketChannel socketChannel = (SocketChannel) key.channel(); String requestHeader = ""; //拿出通道中的Http頭請求 try { requestHeader = receive(socketChannel); } catch (Exception e) { logger.error("讀取socketChannel出錯"); return; } //啓動線程處理該請求,if條件判斷一下,防止心跳包 if(requestHeader.length() > 0) { logger.info("該請求的頭格式爲\r\n" + requestHeader); logger.info("啓動了子線程.."); new Thread(new HttpHandler(requestHeader, key)).start(); } } else if (key.isWritable()) { //該key有Write事件 logger.info("有流寫出!"); SocketChannel socketChannel = (SocketChannel) key.channel(); socketChannel.shutdownInput(); socketChannel.close(); } //從key集合中刪除key,這一步很重要,就是由於沒寫這句,Selector.select()方法一直返回的是0 //緣由分析多是不從集合中刪除,就不會回到I/O就緒事件中 iterator.remove(); } } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private String receive(SocketChannel socketChannel) throws Exception { //聲明一個1024大小的緩衝區 ByteBuffer buffer = ByteBuffer.allocate(1024); byte[] bytes = null; int size = 0; //定義一個字節數組輸出流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //將socketChannel中的數據寫入到buffer中,此時的buffer爲寫模式,size爲寫了多少個字節 while ((size = socketChannel.read(buffer)) > 0) { //將寫模式改成讀模式 //The limit is set to the current position and then the position is set to zero. //將limit設置爲以前的position,而將position置爲0,更多java nio的知識會寫成博客的 buffer.flip(); bytes = new byte[size]; //將Buffer寫入到字節數組中 buffer.get(bytes); //將字節數組寫入到字節緩衝流中 baos.write(bytes); //清空緩衝區 buffer.clear(); } //將流轉回字節數組 bytes = baos.toByteArray(); return new String(bytes); } }
實現MapHandler,解析web.xml,完成uri到對應handler的映射,該handler使用單例完成數組
package com.cszjo.com.http.handler; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.dom4j.Element; import com.cszjo.com.http.utils.XMLUtil; /** * @Title: HandlerMap.java * @Description: HandlerMap(單例) 訪問路徑--->相應解決類 * @author: Han * @date: 2016年7月15日 下午4:52:29 */ public class MapHandler { //訪問路徑對應控制類 private static Map<String, Handler> handlerMap = new HashMap<>(); private static MapHandler instance = null; //將構造器私有化 private MapHandler(){} //獲得HandlerMap對象實例 public static MapHandler getContextMapInstance() { if(instance == null) { synchronized (MapHandler.class) { if(instance == null) { instance = new MapHandler(); //獲得web.xml的根路徑 Element rootElement = XMLUtil.getRootElement("web.xml"); //獲得handler的集合 List<Element> handlers = XMLUtil.getElements(rootElement); for (Element element : handlers) { Element urlPattenEle = XMLUtil.getElement(element, "url-patten"); //獲得urlPatten(uri) String urlPatten = XMLUtil.getElementText(urlPattenEle); Element handlerClazzEle = XMLUtil.getElement(element, "handler-class"); //獲得handler 的class文件路徑 String clazzPath = XMLUtil.getElementText(handlerClazzEle); Class<?> clazz = null; try { //經過反射獲得handler實例化對象,而後以鍵值對的形式存儲 clazz = Class.forName(clazzPath); Handler handler = (Handler)clazz.newInstance(); instance.getHandlerMap().put(urlPatten, handler); Logger.getLogger(MapHandler.class).info("成功添加Handler " + clazzPath); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } return instance; } public Map<String, Handler> getHandlerMap() { return handlerMap; } }
web.xml瀏覽器
<?xml version="1.0" encoding="UTF-8"?> <server> <handler> <handler-class>com.cszjo.com.http.handler.impl.LogionHandler</handler-class> <url-patten>/login</url-patten> </handler> </server>
httpHandler,處理一次http請求,經過uri啓動不一樣的handler進行處理服務器
package com.cszjo.com.http.handler; import java.nio.channels.SelectionKey; import org.apache.log4j.Logger; import com.cszjo.com.http.context.Context; import com.cszjo.com.http.context.impl.HttpContext; import com.cszjo.com.http.handler.impl.NotFoundHandler; /** * @Title: HandlerHttp.java * @Description: 處理一次Http請求 * @author: Han * @date: 2016年7月15日 下午7:07:21 */ public class HttpHandler implements Runnable { //就緒的I/O鍵 private SelectionKey key; //上下文 private Context context = new HttpContext(); //http請求字符串 private String requestHeader; //針對uri選擇不一樣的處理器 private Handler handler; private Logger logger = Logger.getLogger(HttpHandler.class); public HttpHandler(String requestHeader, SelectionKey key) { this.key = key; this.requestHeader = requestHeader; } @Override public void run() { //初始化上下文 context.setContext(requestHeader, key); //獲得uri String uri = context.getRequest().getUri(); logger.info("獲得了uri " + uri); //獲得MapHandler集合(uri-->handler) handler = MapHandler.getContextMapInstance().getHandlerMap().get(uri); //找不到對應的handler if(handler == null) { //404Handler進行處理 handler = new NotFoundHandler(); } //初始化handler並執行 handler.init(context); } }
Context上下文抽象類設計app
package com.cszjo.com.http.context; import java.nio.channels.SelectionKey; /** * @Title: Context.java * @Description: Http上下文抽象類 * @author: Han * @date: 2016年7月16日 下午2:19:06 */ public abstract class Context { protected Request request; protected Response response; /** * 設置當前鏈接的上下文 * @param: @return * @return: Context * @Autor: Han */ public abstract void setContext(String requestHeader, SelectionKey key); /** * 獲得Request * @param: @return * @return: Request * @Autor: Han */ public Request getRequest() { return request; } /** * 獲得Response * @param: @return * @return: Response * @Autor: Han */ public Response getResponse() { return response; } }
HttpContext的實現
package com.cszjo.com.http.context.impl; import java.nio.channels.SelectionKey; import com.cszjo.com.http.context.Context; import com.cszjo.com.http.context.Request; import com.cszjo.com.http.context.Response; /** * @Title: HttpContext.java * @Description: HttpContext http上下文 * @author: Han * @date: 2016年7月16日 下午2:20:00 */ public class HttpContext extends Context { private Request request; private Response response; @Override public void setContext(String requestHeader, SelectionKey key) { //初始化request request = new HttpRequest(requestHeader); //初始化response response = new HttpResponse(key); setRequest(); setResponse(); } private void setRequest() { super.request = this.request; } private void setResponse() { super.response = this.response; } }
Request接口設計
package com.cszjo.com.http.context; import java.util.Map; import java.util.Set; /** * @Title: Request.java * @Description: 接口設計:Request接口 * @author: Han * @date: 2016年7月15日 下午9:21:45 */ public interface Request { public static final String POST = "POST"; public static final String GET = "GET"; /** * 獲得參數 * @param: @return * @return: Map<String,Object> * @Autor: Han */ public Map<String, Object> getAttribute(); /** * 獲得請求方式 * @param: @return * @return: String * @Autor: Han */ public String getMethod(); /** * 獲得URI * @param: @return * @return: String * @Autor: Han */ public String getUri(); /** * 版本協議 * @param: @return * @return: String * @Autor: Han */ public String getProtocol(); /** * 獲得請求頭Map * @param: @return * @return: String * @Autor: Han */ public Map<String, Object> getHeaders(); /** * 獲得請求頭參數集合 * @param: @return * @return: String * @Autor: Han */ public Set<String> getHeaderNames(); /** * 根據請求頭名獲得對應的請求頭 * @param: @return * @return: String * @Autor: Han */ public Object getHeader(String key); }
HttpRequest實現
package com.cszjo.com.http.context.impl; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.cszjo.com.http.context.Request; /** * @Title: HttpRequest.java * @Description: HTTP請求(還有不少方法能夠寫的) * @author: Han * @date: 2016年7月15日 下午9:16:45 */ public class HttpRequest implements Request { //參數 private Map<String, Object> attribute = new HashMap<>(); //請求頭(Request Header) private Map<String, Object> headers = new HashMap<>(); //請求方法 private String method; //uri private String uri; //協議版本 private String protocol; public HttpRequest(String httpHeader) { init(httpHeader); } private void init(String httpHeader) { //將請求分行 String[] headers = httpHeader.split("\r\n"); //設置請求方式 initMethod(headers[0]); //設置URI initURI(headers[0]); //設置版本協議 initProtocol(headers[0]); //設置請求頭 initRequestHeaders(headers); } /** * 設置請求方法 * @param: @param str * @return: void * @Autor: Han */ private void initMethod(String str) { method = str.substring(0, str.indexOf(" ")); } /** * 設置request參數 * @param: @param attr * @return: void * @Autor: Han */ private void initAttribute(String attr) { String[] attrs = attr.split("&"); for (String string : attrs) { String key = string.substring(0, string.indexOf("=")); String value = string.substring(string.indexOf("=") + 1); attribute.put(key, value); } } /** * 設置uri * @param: @param str * @return: void * @Autor: Han */ private void initURI(String str) { uri = str.substring(str.indexOf(" ") + 1, str.indexOf(" ", str.indexOf(" ") + 1)); //若是是get方法,則後面跟着參數 /index?a=1&b=2 if(method.toUpperCase().equals("GET")) { //有問號表示後面跟有參數 if(uri.contains("?")) { String attr = uri.substring(uri.indexOf("?") + 1, uri.length()); uri = uri.substring(0, uri.indexOf("?")); initAttribute(attr); } } } /** * 初始化請求頭 * @param: @param strs * @return: void * @Autor: Han */ private void initRequestHeaders(String[] strs) { //去掉第一行 for(int i = 1; i < strs.length; i++) { String key = strs[i].substring(0, strs[i].indexOf(":")); String value = strs[i].substring(strs[i].indexOf(":") + 1); headers.put(key, value); } } /** * 設置協議版本 * @param: @param str * @return: void * @Autor: Han */ private void initProtocol(String str) { protocol = str.substring(str.lastIndexOf(" ") + 1, str.length()); } @Override public Map<String, Object> getAttribute() { return attribute; } @Override public String getMethod() { return method; } @Override public String getUri() { return uri; } @Override public String getProtocol() { return protocol; } @Override public Map<String, Object> getHeaders() { return headers; } @Override public Set<String> getHeaderNames() { return headers.keySet(); } @Override public Object getHeader(String key) { return headers.get(key); } }
Response接口設計
package com.cszjo.com.http.context; import java.nio.channels.SelectionKey; import com.cszjo.com.http.utils.XMLUtil; /** * @Title: Response.java * @Description: 接口設計:response接口 * @author: Han * @date: 2016年7月16日 下午2:19:25 */ public interface Response { //服務器名字 public static final String SERVER_NAME = XMLUtil.getRootElement("server.xml").element("serverName").getText(); public String getContentType(); public int getStatuCode(); public String getStatuCodeStr(); public String getHtmlFile(); public void setHtmlFile(String htmlFile); public SelectionKey getKey(); public void setContentType(String contentType); public void setStatuCode(int statuCode); public void setStatuCodeStr(String statuCodeStr); }
httpResponse實現
package com.cszjo.com.http.context.impl; import java.nio.channels.SelectionKey; import com.cszjo.com.http.context.Response; /** * @Title: HttpResponse.java * @Description: http響應 * @author: Han * @date: 2016年7月16日 下午2:20:41 */ public class HttpResponse implements Response { private SelectionKey key; //內容類型 defalut 爲text/html private String contentType = "text/html"; //響應碼 defalut 爲200 private int StatuCode = 200; private String statuCodeStr = "OK"; private String htmlFile = ""; public HttpResponse(SelectionKey key) { this.key = key; } @Override public String getContentType() { return contentType; } @Override public int getStatuCode() { return StatuCode; } @Override public SelectionKey getKey() { return key; } @Override public String getStatuCodeStr() { return statuCodeStr; } @Override public String getHtmlFile() { return htmlFile; } @Override public void setHtmlFile(String htmlFile) { this.htmlFile = htmlFile; } @Override public void setContentType(String contentType) { this.contentType = contentType; } @Override public void setStatuCode(int statuCode) { StatuCode = statuCode; } @Override public void setStatuCodeStr(String statuCodeStr) { this.statuCodeStr = statuCodeStr; } }
處理器Handler的接口設計
package com.cszjo.com.http.handler; import com.cszjo.com.http.context.Context; /** * @Title: Handler.java * @Description: 接口設計:處理器Handler接口 * @author: Han * @date: 2016年7月12日 下午7:12:37 */ public interface Handler { /** * 初始化handler * @param: @param context * @return: void * @Autor: Han */ public void init(Context context); /** * handler service(service應該不是這樣作的... - -!) * @param: @param context * @return: void * @Autor: Han */ public void service(Context context); /** * Get形式執行該方法 * @param: @param context * @return: void * @Autor: Han */ public void doGet(Context context); /** * POST形式執行該方法 * @param: @param context * @return: void * @Autor: Han */ public void doPost(Context context); /** * 銷燬Handler(並無銷燬... - -!) * @param: @param context * @return: void * @Autor: Han */ public void destory(Context context); }
由於doGet或者doPost只會執行一個,因此中間在寫一個抽象類,具體的handler只須要重寫該抽象類的方法既可
package com.cszjo.com.http.handler.abs; import com.cszjo.com.http.context.Context; import com.cszjo.com.http.context.Request; import com.cszjo.com.http.handler.Handler; import com.cszjo.com.http.handler.ResponseHandler; /** * @Title: AbstractHandler.java * @Description: Handler抽象類 * @author: Han * @date: 2016年7月16日 下午2:11:57 */ public class AbstractHandler implements Handler { protected Context context; @Override public void init(Context context) { this.context = context; this.service(context); } @Override public void service(Context context) { //經過請求方式選擇具體解決方法 String method = context.getRequest().getMethod(); if(method.equals(Request.GET)) { this.doGet(context); } else if (method.equals(Request.POST)) { this.doPost(context); } sendResponse(context); } @Override public void doGet(Context context) { } @Override public void doPost(Context context) { } @Override public void destory(Context context) { context = null; } /** * 經過上下文,返回封裝response響應 * @param: @param context * @return: void * @Autor: Han */ private void sendResponse(Context context) { new ResponseHandler().write(context); } }
Login Handler的實現
package com.cszjo.com.http.handler.impl; import org.apache.log4j.Logger; import com.cszjo.com.http.context.Context; import com.cszjo.com.http.handler.abs.AbstractHandler; /** * @Title: LogionHandler.java * @Description: 解決login業務邏輯 * @author: Han * @date: 2016年7月16日 下午2:08:18 */ public class LogionHandler extends AbstractHandler{ private Logger logger = Logger.getLogger(LogionHandler.class); @Override public void doGet(Context context) { logger.info("進入了handler--->LoginHandler"); context.getResponse().setHtmlFile("login.html"); } }
未找到請求的URI,因此返回404,該處理器處理404錯誤
package com.cszjo.com.http.handler.impl; import org.apache.log4j.Logger; import com.cszjo.com.http.context.Context; import com.cszjo.com.http.context.Response; import com.cszjo.com.http.handler.abs.AbstractHandler; /** * @Title: NotFoundHandler.java * @Description: 解決404NotFound響應 * @author: Han * @date: 2016年7月16日 下午2:08:44 */ public class NotFoundHandler extends AbstractHandler { private Logger logger = Logger.getLogger(NotFoundHandler.class); private Response response; @Override public void doGet(Context context) { logger.info("進入了404Handler"); response = context.getResponse(); response.setStatuCode(404); response.setStatuCodeStr("Not Found"); response.setHtmlFile("404.html"); } }
封裝完http請求,下一步就須要還原response響應了
package com.cszjo.com.http.handler; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Date; import org.apache.log4j.Logger; import com.cszjo.com.http.context.Context; import com.cszjo.com.http.context.Request; import com.cszjo.com.http.context.Response; /** * @Title: ResponseHandler.java * @Description: 封裝response響應 * @author: Han * @date: 2016年7月16日 下午2:09:45 */ public class ResponseHandler { private Request request; private Response response; private String protocol; private int statuCode; private String statuCodeStr; private ByteBuffer buffer; private String serverName; private String contentType; private SocketChannel channel; private Selector selector; private SelectionKey key; private Logger logger = Logger.getLogger(ResponseHandler.class); private BufferedReader reader; private String htmlFile; public void write(Context context) { //從context中獲得相應的參數 request = context.getRequest(); response = context.getResponse(); buffer = ByteBuffer.allocate(1024); protocol = request.getProtocol(); statuCode = response.getStatuCode(); statuCodeStr = response.getStatuCodeStr(); serverName = Response.SERVER_NAME; contentType = response.getContentType(); key = response.getKey(); selector = key.selector(); channel = (SocketChannel)key.channel(); htmlFile = response.getHtmlFile(); //獲得響應正文內容 String html = setHtml(context); StringBuilder sb = new StringBuilder(); //狀態行 sb.append(protocol + " " + statuCode + " " + statuCodeStr + "\r\n"); //響應頭 sb.append("Server: " + serverName + "\r\n"); sb.append("Content-Type: " + contentType + "\r\n"); sb.append("Date: " + new Date() + "\r\n"); if(reader != null) { sb.append("Content-Length: " + html.getBytes().length + "\r\n"); } //響應內容 sb.append("\r\n"); sb.append(html); buffer.put(sb.toString().getBytes()); //從寫模式,切換到讀模式 buffer.flip(); try { logger.info("生成相應\r\n" + sb.toString()); channel.register(selector, SelectionKey.OP_WRITE); channel.write(buffer); } catch (IOException e) { e.printStackTrace(); } } private String setHtml(Context context) { StringBuilder html = null; if(htmlFile != null && htmlFile.length() > 0) { html = new StringBuilder(); try { reader = new BufferedReader(new FileReader(new File(htmlFile))); String htmlStr; htmlStr = reader.readLine(); while(htmlStr != null) { html.append(htmlStr + "\r\n"); htmlStr = reader.readLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return html.toString(); } }
程序啓動入口
package com.cszjo.com.http.server; /** * @Title: Solution.java * @Description: 啓動Web服務器入口 * @author: Han * @date: 2016年7月12日 下午7:11:15 */ public class Solution { //啓動方法 public static void main(String[] args) { new Thread(new Server(false)).start(); } }
XMLUtils
package com.cszjo.com.http.utils; import java.io.File; import java.util.List; import org.apache.log4j.Logger; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * @Title: XMLUtil.java * @Description: 解決XML讀取問題 * @author: Han * @date: 2016年7月15日 下午4:55:28 */ public class XMLUtil { private static Logger logger = Logger.getLogger(XMLUtil.class); private static SAXReader reader = new SAXReader(); /** * 獲得根節點 * @param: @param xmlPath * @param: @return * @return: Element * @Autor: Han */ public static Element getRootElement(String xmlPath) { Document document = null;; try { document = reader.read(new File(xmlPath)); } catch (DocumentException e) { logger.error("找不到指定的xml文件的路徑" + xmlPath + "!"); return null; } return document.getRootElement(); } /** * 獲得該節點下的子節點集合 * @param: @param element * @param: @return * @return: List<Element> * @Autor: Han */ @SuppressWarnings("unchecked") public static List<Element> getElements(Element element) { return element.elements(); } /** * 獲得該節點下指定的節點 * @param: @param name * @param: @return * @return: Element * @Autor: Han */ public static Element getElement(Element element, String name) { Element childElement = element.element(name); if(childElement == null) { logger.error(element.getName() + "節點下沒有子節點" + name); return null; } return childElement; } /** * 獲得該節點的內容 * @param: @param element * @param: @return * @return: String * @Autor: Han */ public static String getElementText(Element element) { return element.getText(); } }
該項目須要用到的兩個包:log4j,dom4j
其他配置文件和靜態文件
log4j.properties
### \u8BBE\u7F6E###
log4j.rootLogger = debug,stdout,D,E
### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
server.xml
<?xml version="1.0" encoding="UTF-8"?> <server> <port>8089</port> <serverName>Han`s Server</serverName> <!-- 默認編碼爲UTF-8 --> <charset>UTF-8</charset> </server>
login.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="Generator" content="EditPlus®"> <meta name="Author" content=""> <meta name="Keywords" content=""> <meta name="Description" content=""> <title>han登陸</title> </head> <body> 用戶名:<input type="text" name="userName"><br/> 密碼:<input type="text" name="userName"><br/> </body> </html>
404.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="Generator" content="EditPlus®"> <meta name="Author" content=""> <meta name="Keywords" content=""> <meta name="Description" content=""> <title>Document</title> </head> <body> <h1>404 NOT Found</h1> <strong style="color:red;">來自Han服務器</strong> </body> </html>
啓動服務
在瀏覽器中輸入http://localhost:8089/login以後
瀏覽器顯示
在瀏覽器中輸入http://localhost:8089/lo以後
OK,成功!