├── 404.html ├── README.md ├── lib ├── dom4j-1.6.1.jar └── log4j-1.2.17.jar ├── login.html ├── server.xml ├── src ├── com │ └── cszjo │ │ └── com │ │ └── http │ │ ├── context │ │ ├── Context.java │ │ ├── Request.java │ │ ├── Response.java │ │ └── impl │ │ │ ├── HttpContext.java │ │ │ ├── HttpRequest.java │ │ │ └── HttpResponse.java │ │ ├── handler │ │ ├── Handler.java │ │ ├── HttpHandler.java │ │ ├── MapHandler.java │ │ ├── ResponseHandler.java │ │ ├── abs │ │ │ └── AbstractHandler.java │ │ └── impl │ │ │ ├── LogionHandler.java │ │ │ └── NotFoundHandler.java │ │ ├── server │ │ ├── Server.java │ │ └── Solution.java │ │ ├── test │ │ ├── HttpRequestTest.java │ │ └── XMLUtilTest.java │ │ └── utils │ │ └── XMLUtil.java └── log4j.properties └── web.xml /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 |

404 NOT Found

13 | 来自Han服务器 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HttpServerByJavaNIO 2 | 基于JavaNIO实现的Http服务器,涉及到Http Request和Http Response的解析,封装,模仿servlet实现handler处理对应的URI。 3 | 4 | # 详细讲解请看我在博客园写的博客: 5 | http://www.cnblogs.com/a294098789/p/5676566.html 6 | -------------------------------------------------------------------------------- /lib/dom4j-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansiming/HttpServerByJavaNIO/02c6f5026fb15473da73f7a7cad1009f95ab050c/lib/dom4j-1.6.1.jar -------------------------------------------------------------------------------- /lib/log4j-1.2.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansiming/HttpServerByJavaNIO/02c6f5026fb15473da73f7a7cad1009f95ab050c/lib/log4j-1.2.17.jar -------------------------------------------------------------------------------- /login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | han登录 10 | 11 | 12 | 用户名:
13 | 密码:
14 | 15 | 16 | -------------------------------------------------------------------------------- /server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8089 4 | Han`s Server 5 | 6 | UTF-8 7 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/context/Context.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.context; 2 | 3 | import java.nio.channels.SelectionKey; 4 | 5 | /** 6 | * @Title: Context.java 7 | * @Description: Http上下文抽象类 8 | * @author: Han 9 | * @date: 2016年7月16日 下午2:19:06 10 | */ 11 | public abstract class Context { 12 | 13 | protected Request request; 14 | protected Response response; 15 | 16 | /** 17 | * 设置当前连接的上下文 18 | * @param: @return 19 | * @return: Context 20 | * @Autor: Han 21 | */ 22 | public abstract void setContext(String requestHeader, SelectionKey key); 23 | 24 | /** 25 | * 得到Request 26 | * @param: @return 27 | * @return: Request 28 | * @Autor: Han 29 | */ 30 | public Request getRequest() { 31 | return request; 32 | } 33 | 34 | /** 35 | * 得到Response 36 | * @param: @return 37 | * @return: Response 38 | * @Autor: Han 39 | */ 40 | public Response getResponse() { 41 | return response; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/context/Request.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.context; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | /** 7 | * @Title: Request.java 8 | * @Description: 接口设计:Request接口 9 | * @author: Han 10 | * @date: 2016年7月15日 下午9:21:45 11 | */ 12 | public interface Request { 13 | 14 | public static final String POST = "POST"; 15 | 16 | public static final String GET = "GET"; 17 | /** 18 | * 得到参数 19 | * @param: @return 20 | * @return: Map 21 | * @Autor: Han 22 | */ 23 | public Map getAttribute(); 24 | 25 | /** 26 | * 得到请求方式 27 | * @param: @return 28 | * @return: String 29 | * @Autor: Han 30 | */ 31 | public String getMethod(); 32 | 33 | /** 34 | * 得到URI 35 | * @param: @return 36 | * @return: String 37 | * @Autor: Han 38 | */ 39 | public String getUri(); 40 | 41 | /** 42 | * 版本协议 43 | * @param: @return 44 | * @return: String 45 | * @Autor: Han 46 | */ 47 | public String getProtocol(); 48 | 49 | /** 50 | * 得到请求头Map 51 | * @param: @return 52 | * @return: String 53 | * @Autor: Han 54 | */ 55 | public Map getHeaders(); 56 | 57 | /** 58 | * 得到请求头参数集合 59 | * @param: @return 60 | * @return: String 61 | * @Autor: Han 62 | */ 63 | public Set getHeaderNames(); 64 | 65 | /** 66 | * 根据请求头名得到对应的请求头 67 | * @param: @return 68 | * @return: String 69 | * @Autor: Han 70 | */ 71 | public Object getHeader(String key); 72 | } 73 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/context/Response.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.context; 2 | 3 | import java.nio.channels.SelectionKey; 4 | 5 | import com.cszjo.com.http.utils.XMLUtil; 6 | 7 | /** 8 | * @Title: Response.java 9 | * @Description: 接口设计:response接口 10 | * @author: Han 11 | * @date: 2016年7月16日 下午2:19:25 12 | */ 13 | public interface Response { 14 | 15 | //服务器名字 16 | public static final String SERVER_NAME = XMLUtil.getRootElement("server.xml").element("serverName").getText(); 17 | 18 | public String getContentType(); 19 | 20 | public int getStatuCode(); 21 | 22 | public String getStatuCodeStr(); 23 | 24 | public String getHtmlFile(); 25 | 26 | public void setHtmlFile(String htmlFile); 27 | 28 | public SelectionKey getKey(); 29 | 30 | public void setContentType(String contentType); 31 | 32 | public void setStatuCode(int statuCode); 33 | 34 | public void setStatuCodeStr(String statuCodeStr); 35 | } 36 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/context/impl/HttpContext.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.context.impl; 2 | 3 | import java.nio.channels.SelectionKey; 4 | 5 | import com.cszjo.com.http.context.Context; 6 | import com.cszjo.com.http.context.Request; 7 | import com.cszjo.com.http.context.Response; 8 | 9 | /** 10 | * @Title: HttpContext.java 11 | * @Description: HttpContext http上下文 12 | * @author: Han 13 | * @date: 2016年7月16日 下午2:20:00 14 | */ 15 | public class HttpContext extends Context { 16 | 17 | private Request request; 18 | private Response response; 19 | 20 | @Override 21 | public void setContext(String requestHeader, SelectionKey key) { 22 | 23 | //初始化request 24 | request = new HttpRequest(requestHeader); 25 | //初始化response 26 | response = new HttpResponse(key); 27 | setRequest(); 28 | setResponse(); 29 | } 30 | 31 | private void setRequest() { 32 | super.request = this.request; 33 | } 34 | 35 | private void setResponse() { 36 | super.response = this.response; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/context/impl/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.context.impl; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | import com.cszjo.com.http.context.Request; 8 | 9 | /** 10 | * @Title: HttpRequest.java 11 | * @Description: HTTP请求(还有很多方法可以写的) 12 | * @author: Han 13 | * @date: 2016年7月15日 下午9:16:45 14 | */ 15 | public class HttpRequest implements Request { 16 | 17 | //参数 18 | private Map attribute = new HashMap<>(); 19 | 20 | //请求头(Request Header) 21 | private Map headers = new HashMap<>(); 22 | 23 | //请求方法 24 | private String method; 25 | 26 | //uri 27 | private String uri; 28 | 29 | //协议版本 30 | private String protocol; 31 | 32 | public HttpRequest(String httpHeader) { 33 | init(httpHeader); 34 | } 35 | 36 | private void init(String httpHeader) { 37 | //将请求分行 38 | String[] headers = httpHeader.split("\r\n"); 39 | //设置请求方式 40 | initMethod(headers[0]); 41 | //设置URI 42 | initURI(headers[0]); 43 | //设置版本协议 44 | initProtocol(headers[0]); 45 | //设置请求头 46 | initRequestHeaders(headers); 47 | } 48 | 49 | /** 50 | * 设置请求方法 51 | * @param: @param str 52 | * @return: void 53 | * @Autor: Han 54 | */ 55 | private void initMethod(String str) { 56 | method = str.substring(0, str.indexOf(" ")); 57 | } 58 | 59 | /** 60 | * 设置request参数 61 | * @param: @param attr 62 | * @return: void 63 | * @Autor: Han 64 | */ 65 | private void initAttribute(String attr) { 66 | String[] attrs = attr.split("&"); 67 | for (String string : attrs) { 68 | String key = string.substring(0, string.indexOf("=")); 69 | String value = string.substring(string.indexOf("=") + 1); 70 | attribute.put(key, value); 71 | } 72 | } 73 | 74 | /** 75 | * 设置uri 76 | * @param: @param str 77 | * @return: void 78 | * @Autor: Han 79 | */ 80 | private void initURI(String str) { 81 | uri = str.substring(str.indexOf(" ") + 1, str.indexOf(" ", str.indexOf(" ") + 1)); 82 | //如果是get方法,则后面跟着参数 /index?a=1&b=2 83 | if(method.toUpperCase().equals("GET")) { 84 | //有问号表示后面跟有参数 85 | if(uri.contains("?")) { 86 | String attr = uri.substring(uri.indexOf("?") + 1, uri.length()); 87 | uri = uri.substring(0, uri.indexOf("?")); 88 | initAttribute(attr); 89 | } 90 | } 91 | } 92 | 93 | /** 94 | * 初始化请求头 95 | * @param: @param strs 96 | * @return: void 97 | * @Autor: Han 98 | */ 99 | private void initRequestHeaders(String[] strs) { 100 | //去掉第一行 101 | for(int i = 1; i < strs.length; i++) { 102 | String key = strs[i].substring(0, strs[i].indexOf(":")); 103 | String value = strs[i].substring(strs[i].indexOf(":") + 1); 104 | headers.put(key, value); 105 | } 106 | } 107 | 108 | /** 109 | * 设置协议版本 110 | * @param: @param str 111 | * @return: void 112 | * @Autor: Han 113 | */ 114 | private void initProtocol(String str) { 115 | protocol = str.substring(str.lastIndexOf(" ") + 1, str.length()); 116 | } 117 | 118 | @Override 119 | public Map getAttribute() { 120 | return attribute; 121 | } 122 | 123 | @Override 124 | public String getMethod() { 125 | return method; 126 | } 127 | 128 | @Override 129 | public String getUri() { 130 | return uri; 131 | } 132 | 133 | @Override 134 | public String getProtocol() { 135 | return protocol; 136 | } 137 | 138 | @Override 139 | public Map getHeaders() { 140 | return headers; 141 | } 142 | 143 | @Override 144 | public Set getHeaderNames() { 145 | return headers.keySet(); 146 | } 147 | 148 | @Override 149 | public Object getHeader(String key) { 150 | return headers.get(key); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/context/impl/HttpResponse.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.context.impl; 2 | 3 | import java.nio.channels.SelectionKey; 4 | 5 | import com.cszjo.com.http.context.Response; 6 | 7 | /** 8 | * @Title: HttpResponse.java 9 | * @Description: http响应 10 | * @author: Han 11 | * @date: 2016年7月16日 下午2:20:41 12 | */ 13 | public class HttpResponse implements Response { 14 | 15 | private SelectionKey key; 16 | //内容类型 defalut 为text/html 17 | private String contentType = "text/html"; 18 | //响应码 defalut 为200 19 | private int StatuCode = 200; 20 | private String statuCodeStr = "OK"; 21 | private String htmlFile = ""; 22 | 23 | public HttpResponse(SelectionKey key) { 24 | this.key = key; 25 | } 26 | 27 | @Override 28 | public String getContentType() { 29 | return contentType; 30 | } 31 | 32 | @Override 33 | public int getStatuCode() { 34 | return StatuCode; 35 | } 36 | 37 | @Override 38 | public SelectionKey getKey() { 39 | return key; 40 | } 41 | 42 | @Override 43 | public String getStatuCodeStr() { 44 | return statuCodeStr; 45 | } 46 | 47 | @Override 48 | public String getHtmlFile() { 49 | return htmlFile; 50 | } 51 | 52 | @Override 53 | public void setHtmlFile(String htmlFile) { 54 | this.htmlFile = htmlFile; 55 | } 56 | 57 | @Override 58 | public void setContentType(String contentType) { 59 | this.contentType = contentType; 60 | } 61 | 62 | @Override 63 | public void setStatuCode(int statuCode) { 64 | StatuCode = statuCode; 65 | } 66 | 67 | @Override 68 | public void setStatuCodeStr(String statuCodeStr) { 69 | this.statuCodeStr = statuCodeStr; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/Handler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler; 2 | 3 | import com.cszjo.com.http.context.Context; 4 | 5 | /** 6 | * @Title: Handler.java 7 | * @Description: 接口设计:处理器Handler接口 8 | * @author: Han 9 | * @date: 2016年7月12日 下午7:12:37 10 | */ 11 | public interface Handler { 12 | 13 | /** 14 | * 初始化handler 15 | * @param: @param context 16 | * @return: void 17 | * @Autor: Han 18 | */ 19 | public void init(Context context); 20 | 21 | /** 22 | * handler service(service应该不是这样做的... - -!) 23 | * @param: @param context 24 | * @return: void 25 | * @Autor: Han 26 | */ 27 | public void service(Context context); 28 | 29 | /** 30 | * Get形式执行该方法 31 | * @param: @param context 32 | * @return: void 33 | * @Autor: Han 34 | */ 35 | public void doGet(Context context); 36 | 37 | /** 38 | * POST形式执行该方法 39 | * @param: @param context 40 | * @return: void 41 | * @Autor: Han 42 | */ 43 | public void doPost(Context context); 44 | 45 | /** 46 | * 销毁Handler(并没有销毁... - -!) 47 | * @param: @param context 48 | * @return: void 49 | * @Autor: Han 50 | */ 51 | public void destory(Context context); 52 | } 53 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/HttpHandler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler; 2 | 3 | import java.nio.channels.SelectionKey; 4 | 5 | import org.apache.log4j.Logger; 6 | 7 | import com.cszjo.com.http.context.Context; 8 | import com.cszjo.com.http.context.impl.HttpContext; 9 | import com.cszjo.com.http.handler.impl.NotFoundHandler; 10 | 11 | /** 12 | * @Title: HandlerHttp.java 13 | * @Description: 处理一次Http请求 14 | * @author: Han 15 | * @date: 2016年7月15日 下午7:07:21 16 | */ 17 | public class HttpHandler implements Runnable { 18 | 19 | //就绪的I/O键 20 | private SelectionKey key; 21 | //上下文 22 | private Context context = new HttpContext(); 23 | //http请求字符串 24 | private String requestHeader; 25 | //针对uri选择不同的处理器 26 | private Handler handler; 27 | private Logger logger = Logger.getLogger(HttpHandler.class); 28 | 29 | public HttpHandler(String requestHeader, SelectionKey key) { 30 | this.key = key; 31 | this.requestHeader = requestHeader; 32 | } 33 | 34 | @Override 35 | public void run() { 36 | //初始化上下文 37 | context.setContext(requestHeader, key); 38 | //得到uri 39 | String uri = context.getRequest().getUri(); 40 | logger.info("得到了uri " + uri); 41 | //得到MapHandler集合(uri-->handler) 42 | handler = MapHandler.getContextMapInstance().getHandlerMap().get(uri); 43 | //找不到对应的handler 44 | if(handler == null) { 45 | //404Handler进行处理 46 | handler = new NotFoundHandler(); 47 | } 48 | //初始化handler并执行 49 | handler.init(context); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/MapHandler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import org.apache.log4j.Logger; 8 | import org.dom4j.Element; 9 | 10 | import com.cszjo.com.http.utils.XMLUtil; 11 | 12 | /** 13 | * @Title: HandlerMap.java 14 | * @Description: HandlerMap(单例) 访问路径--->相应解决类 15 | * @author: Han 16 | * @date: 2016年7月15日 下午4:52:29 17 | */ 18 | public class MapHandler { 19 | 20 | //访问路径对应控制类 21 | private static Map handlerMap = new HashMap<>(); 22 | 23 | private static MapHandler instance = null; 24 | 25 | //将构造器私有化 26 | private MapHandler(){} 27 | 28 | //得到HandlerMap对象实例 29 | public static MapHandler getContextMapInstance() { 30 | 31 | if(instance == null) { 32 | synchronized (MapHandler.class) { 33 | if(instance == null) { 34 | instance = new MapHandler(); 35 | //得到web.xml的根路径 36 | Element rootElement = XMLUtil.getRootElement("web.xml"); 37 | //得到handler的集合 38 | List handlers = XMLUtil.getElements(rootElement); 39 | for (Element element : handlers) { 40 | Element urlPattenEle = XMLUtil.getElement(element, "url-patten"); 41 | //得到urlPatten(uri) 42 | String urlPatten = XMLUtil.getElementText(urlPattenEle); 43 | Element handlerClazzEle = XMLUtil.getElement(element, "handler-class"); 44 | //得到handler 的class文件路径 45 | String clazzPath = XMLUtil.getElementText(handlerClazzEle); 46 | Class clazz = null; 47 | try { 48 | //通过反射得到handler实例化对象,然后以键值对的形式存储 49 | clazz = Class.forName(clazzPath); 50 | Handler handler = (Handler)clazz.newInstance(); 51 | instance.getHandlerMap().put(urlPatten, handler); 52 | Logger.getLogger(MapHandler.class).info("成功添加Handler " + clazzPath); 53 | } catch (ClassNotFoundException e) { 54 | e.printStackTrace(); 55 | } catch (InstantiationException e) { 56 | e.printStackTrace(); 57 | } catch (IllegalAccessException e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | return instance; 66 | } 67 | 68 | public Map getHandlerMap() { 69 | return handlerMap; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/ResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileReader; 7 | import java.io.IOException; 8 | import java.nio.ByteBuffer; 9 | import java.nio.channels.SelectionKey; 10 | import java.nio.channels.Selector; 11 | import java.nio.channels.SocketChannel; 12 | import java.util.Date; 13 | 14 | import org.apache.log4j.Logger; 15 | 16 | import com.cszjo.com.http.context.Context; 17 | import com.cszjo.com.http.context.Request; 18 | import com.cszjo.com.http.context.Response; 19 | 20 | /** 21 | * @Title: ResponseHandler.java 22 | * @Description: 封装response响应 23 | * @author: Han 24 | * @date: 2016年7月16日 下午2:09:45 25 | */ 26 | public class ResponseHandler { 27 | 28 | private Request request; 29 | private Response response; 30 | private String protocol; 31 | private int statuCode; 32 | private String statuCodeStr; 33 | private ByteBuffer buffer; 34 | private String serverName; 35 | private String contentType; 36 | private SocketChannel channel; 37 | private Selector selector; 38 | private SelectionKey key; 39 | private Logger logger = Logger.getLogger(ResponseHandler.class); 40 | private BufferedReader reader; 41 | private String htmlFile; 42 | 43 | public void write(Context context) { 44 | //从context中得到相应的参数 45 | request = context.getRequest(); 46 | response = context.getResponse(); 47 | buffer = ByteBuffer.allocate(1024); 48 | protocol = request.getProtocol(); 49 | statuCode = response.getStatuCode(); 50 | statuCodeStr = response.getStatuCodeStr(); 51 | serverName = Response.SERVER_NAME; 52 | contentType = response.getContentType(); 53 | key = response.getKey(); 54 | selector = key.selector(); 55 | channel = (SocketChannel)key.channel(); 56 | htmlFile = response.getHtmlFile(); 57 | 58 | //得到响应正文内容 59 | String html = setHtml(context); 60 | 61 | StringBuilder sb = new StringBuilder(); 62 | //状态行 63 | sb.append(protocol + " " + statuCode + " " + statuCodeStr + "\r\n"); 64 | //响应头 65 | sb.append("Server: " + serverName + "\r\n"); 66 | sb.append("Content-Type: " + contentType + "\r\n"); 67 | sb.append("Date: " + new Date() + "\r\n"); 68 | if(reader != null) { 69 | sb.append("Content-Length: " + html.getBytes().length + "\r\n"); 70 | } 71 | 72 | //响应内容 73 | sb.append("\r\n"); 74 | sb.append(html); 75 | 76 | buffer.put(sb.toString().getBytes()); 77 | //从写模式,切换到读模式 78 | buffer.flip(); 79 | try { 80 | logger.info("生成相应\r\n" + sb.toString()); 81 | channel.register(selector, SelectionKey.OP_WRITE); 82 | channel.write(buffer); 83 | } catch (IOException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | 88 | private String setHtml(Context context) { 89 | StringBuilder html = null; 90 | if(htmlFile != null && htmlFile.length() > 0) { 91 | 92 | html = new StringBuilder(); 93 | 94 | try { 95 | reader = new BufferedReader(new FileReader(new File(htmlFile))); 96 | String htmlStr; 97 | htmlStr = reader.readLine(); 98 | while(htmlStr != null) { 99 | html.append(htmlStr + "\r\n"); 100 | htmlStr = reader.readLine(); 101 | } 102 | 103 | } catch (FileNotFoundException e) { 104 | e.printStackTrace(); 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | 110 | return html.toString(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/abs/AbstractHandler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler.abs; 2 | 3 | import com.cszjo.com.http.context.Context; 4 | import com.cszjo.com.http.context.Request; 5 | import com.cszjo.com.http.handler.Handler; 6 | import com.cszjo.com.http.handler.ResponseHandler; 7 | 8 | /** 9 | * @Title: AbstractHandler.java 10 | * @Description: Handler抽象类 11 | * @author: Han 12 | * @date: 2016年7月16日 下午2:11:57 13 | */ 14 | public class AbstractHandler implements Handler { 15 | 16 | protected Context context; 17 | 18 | @Override 19 | public void init(Context context) { 20 | this.context = context; 21 | this.service(context); 22 | } 23 | 24 | @Override 25 | public void service(Context context) { 26 | //通过请求方式选择具体解决方法 27 | String method = context.getRequest().getMethod(); 28 | if(method.equals(Request.GET)) { 29 | this.doGet(context); 30 | } else if (method.equals(Request.POST)) { 31 | this.doPost(context); 32 | } 33 | sendResponse(context); 34 | } 35 | 36 | @Override 37 | public void doGet(Context context) { 38 | 39 | } 40 | 41 | @Override 42 | public void doPost(Context context) { 43 | 44 | } 45 | 46 | @Override 47 | public void destory(Context context) { 48 | context = null; 49 | } 50 | 51 | /** 52 | * 通过上下文,返回封装response响应 53 | * @param: @param context 54 | * @return: void 55 | * @Autor: Han 56 | */ 57 | private void sendResponse(Context context) { 58 | new ResponseHandler().write(context); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/impl/LogionHandler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler.impl; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import com.cszjo.com.http.context.Context; 6 | import com.cszjo.com.http.handler.abs.AbstractHandler; 7 | 8 | /** 9 | * @Title: LogionHandler.java 10 | * @Description: 解决login业务逻辑 11 | * @author: Han 12 | * @date: 2016年7月16日 下午2:08:18 13 | */ 14 | public class LogionHandler extends AbstractHandler{ 15 | 16 | private Logger logger = Logger.getLogger(LogionHandler.class); 17 | 18 | @Override 19 | public void doGet(Context context) { 20 | logger.info("进入了handler--->LoginHandler"); 21 | context.getResponse().setHtmlFile("login.html"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/handler/impl/NotFoundHandler.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.handler.impl; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import com.cszjo.com.http.context.Context; 6 | import com.cszjo.com.http.context.Response; 7 | import com.cszjo.com.http.handler.abs.AbstractHandler; 8 | 9 | /** 10 | * @Title: NotFoundHandler.java 11 | * @Description: 解决404NotFound响应 12 | * @author: Han 13 | * @date: 2016年7月16日 下午2:08:44 14 | */ 15 | public class NotFoundHandler extends AbstractHandler { 16 | 17 | private Logger logger = Logger.getLogger(NotFoundHandler.class); 18 | private Response response; 19 | 20 | @Override 21 | public void doGet(Context context) { 22 | logger.info("进入了404Handler"); 23 | response = context.getResponse(); 24 | 25 | response.setStatuCode(404); 26 | response.setStatuCodeStr("Not Found"); 27 | response.setHtmlFile("404.html"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/server/Server.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.server; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.net.InetSocketAddress; 6 | import java.net.ServerSocket; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.SelectionKey; 9 | import java.nio.channels.Selector; 10 | import java.nio.channels.ServerSocketChannel; 11 | import java.nio.channels.SocketChannel; 12 | import java.util.Iterator; 13 | import java.util.Set; 14 | 15 | import org.apache.log4j.Logger; 16 | 17 | import com.cszjo.com.http.handler.HttpHandler; 18 | import com.cszjo.com.http.utils.XMLUtil; 19 | 20 | /** 21 | * @Title: Server.java 22 | * @Description: 打开服务 23 | * @author: Han 24 | * @date: 2016年7月12日 下午7:22:47 25 | */ 26 | public class Server implements Runnable { 27 | 28 | private boolean interrupted = false; 29 | 30 | private Logger logger = Logger.getLogger(Server.class); 31 | 32 | public Server(boolean interrupted) { 33 | this.interrupted = interrupted; 34 | } 35 | 36 | @Override 37 | public void run() { 38 | try { 39 | //打开一个选择器 40 | Selector selector = Selector.open(); 41 | //打开ServerSocketChannel通道 42 | ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 43 | //得到ServerSocket对象 44 | ServerSocket serverSocket = serverSocketChannel.socket(); 45 | //ServerSocketChannel通道监听server.xml中设置的端口 46 | String portStr = XMLUtil.getRootElement("server.xml").element("port").getText(); 47 | serverSocket.setReuseAddress(true); 48 | try { 49 | serverSocket.bind(new InetSocketAddress(Integer.parseInt(portStr))); 50 | } catch (Exception e) { 51 | logger.error("绑定端口失败,请检查server.xml中是否设置了port属性"); 52 | return; 53 | } 54 | logger.info("成功绑定端口" + portStr); 55 | //将通道设置为非阻塞模式 56 | serverSocketChannel.configureBlocking(false); 57 | //将serverSocketChannel注册给选择器,并绑定ACCEPT事件 58 | serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 59 | 60 | logger.info("服务器启动成功"); 61 | while(!interrupted) { 62 | //查询就绪的通道数量 63 | int readyChannels = selector.select(); 64 | //没有就绪的则继续进行循环 65 | if(readyChannels == 0) 66 | continue; 67 | //获得就绪的selectionKey的set集合 68 | Set keys = selector.selectedKeys(); 69 | //获得set集合的迭代器 70 | Iterator iterator = keys.iterator(); 71 | while(iterator.hasNext()) { 72 | SelectionKey key = iterator.next(); 73 | if(key.isAcceptable()) { 74 | //该key有ACCEPT事件 75 | //将监听得到的channel强转为ServerSocketChannel 76 | ServerSocketChannel server = (ServerSocketChannel) key.channel(); 77 | //得到接收到的SocketChannel 78 | SocketChannel socketChannel = server.accept(); 79 | if(socketChannel != null) { 80 | logger.info("收到了来自" + ((InetSocketAddress)socketChannel.getRemoteAddress()).getHostString() 81 | + "的请求"); 82 | //将socketChannel设置为阻塞模式 83 | socketChannel.configureBlocking(false); 84 | //将socketChannel注册到选择器 85 | socketChannel.register(selector, SelectionKey.OP_READ); 86 | } 87 | } else if (key.isReadable()) { 88 | //该key有Read事件 89 | SocketChannel socketChannel = (SocketChannel) key.channel(); 90 | String requestHeader = ""; 91 | //拿出通道中的Http头请求 92 | try { 93 | requestHeader = receive(socketChannel); 94 | } catch (Exception e) { 95 | logger.error("读取socketChannel出错"); 96 | return; 97 | } 98 | //启动线程处理该请求,if条件判断一下,防止心跳包 99 | if(requestHeader.length() > 0) { 100 | logger.info("该请求的头格式为\r\n" + requestHeader); 101 | logger.info("启动了子线程.."); 102 | new Thread(new HttpHandler(requestHeader, key)).start(); 103 | } 104 | } else if (key.isWritable()) { 105 | //该key有Write事件 106 | logger.info("有流写出!"); 107 | SocketChannel socketChannel = (SocketChannel) key.channel(); 108 | socketChannel.shutdownInput(); 109 | socketChannel.close(); 110 | } 111 | //从key集合中删除key,这一步很重要,就是因为没写这句,Selector.select()方法一直返回的是0 112 | //原因分析可能是不从集合中删除,就不会回到I/O就绪事件中 113 | iterator.remove(); 114 | } 115 | } 116 | 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } catch (Exception e) { 120 | e.printStackTrace(); 121 | } 122 | } 123 | 124 | private String receive(SocketChannel socketChannel) throws Exception { 125 | //声明一个1024大小的缓冲区 126 | ByteBuffer buffer = ByteBuffer.allocate(1024); 127 | byte[] bytes = null; 128 | int size = 0; 129 | //定义一个字节数组输出流 130 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 131 | //将socketChannel中的数据写入到buffer中,此时的buffer为写模式,size为写了多少个字节 132 | while ((size = socketChannel.read(buffer)) > 0) { 133 | //将写模式改为读模式 134 | //The limit is set to the current position and then the position is set to zero. 135 | //将limit设置为之前的position,而将position置为0,更多java nio的知识会写成博客的 136 | buffer.flip(); 137 | bytes = new byte[size]; 138 | //将Buffer写入到字节数组中 139 | buffer.get(bytes); 140 | //将字节数组写入到字节缓冲流中 141 | baos.write(bytes); 142 | //清空缓冲区 143 | buffer.clear(); 144 | } 145 | //将流转回字节数组 146 | bytes = baos.toByteArray(); 147 | return new String(bytes); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/server/Solution.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.server; 2 | 3 | /** 4 | * @Title: Solution.java 5 | * @Description: 启动Web服务器入口 6 | * @author: Han 7 | * @date: 2016年7月12日 下午7:11:15 8 | */ 9 | public class Solution { 10 | 11 | //启动方法 12 | public static void main(String[] args) { 13 | new Thread(new Server(false)).start(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/test/HttpRequestTest.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.test; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | 7 | import org.junit.Test; 8 | 9 | import com.cszjo.com.http.context.Request; 10 | import com.cszjo.com.http.context.impl.HttpRequest; 11 | 12 | public class HttpRequestTest { 13 | 14 | @SuppressWarnings("unused") 15 | @Test 16 | public void testRequest() throws Exception { 17 | BufferedReader reader = new BufferedReader(new FileReader(new File("src/header.txt"))); 18 | String str = reader.readLine(); 19 | String header = ""; 20 | while(str != null) { 21 | header += str + "\r\n"; 22 | str = reader.readLine(); 23 | } 24 | Request request = new HttpRequest(header); 25 | reader.close(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/test/XMLUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.test; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.List; 6 | 7 | import org.dom4j.Element; 8 | import org.junit.Test; 9 | 10 | import com.cszjo.com.http.utils.XMLUtil; 11 | 12 | @SuppressWarnings("unused") 13 | public class XMLUtilTest { 14 | 15 | @Test 16 | public void testGetRootElement() throws Exception { 17 | Element rootElement = XMLUtil.getRootElement("web.xml"); 18 | } 19 | 20 | @Test 21 | public void testgetElements() throws Exception { 22 | Element rootElement = XMLUtil.getRootElement("web.xml"); 23 | List elements = XMLUtil.getElements(rootElement); 24 | } 25 | 26 | @Test 27 | public void testgetElement() throws Exception { 28 | Element rootElement = XMLUtil.getRootElement("web.xml"); 29 | Element element = XMLUtil.getElement(rootElement, "123"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/com/cszjo/com/http/utils/XMLUtil.java: -------------------------------------------------------------------------------- 1 | package com.cszjo.com.http.utils; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | 6 | import org.apache.log4j.Logger; 7 | import org.dom4j.Document; 8 | import org.dom4j.DocumentException; 9 | import org.dom4j.Element; 10 | import org.dom4j.io.SAXReader; 11 | 12 | /** 13 | * @Title: XMLUtil.java 14 | * @Description: 解决XML读取问题 15 | * @author: Han 16 | * @date: 2016年7月15日 下午4:55:28 17 | */ 18 | public class XMLUtil { 19 | 20 | private static Logger logger = Logger.getLogger(XMLUtil.class); 21 | private static SAXReader reader = new SAXReader(); 22 | 23 | /** 24 | * 得到根节点 25 | * @param: @param xmlPath 26 | * @param: @return 27 | * @return: Element 28 | * @Autor: Han 29 | */ 30 | public static Element getRootElement(String xmlPath) { 31 | Document document = null;; 32 | try { 33 | document = reader.read(new File(xmlPath)); 34 | } catch (DocumentException e) { 35 | logger.error("找不到指定的xml文件的路径" + xmlPath + "!"); 36 | return null; 37 | } 38 | return document.getRootElement(); 39 | } 40 | 41 | /** 42 | * 得到该节点下的子节点集合 43 | * @param: @param element 44 | * @param: @return 45 | * @return: List 46 | * @Autor: Han 47 | */ 48 | @SuppressWarnings("unchecked") 49 | public static List getElements(Element element) { 50 | return element.elements(); 51 | } 52 | 53 | /** 54 | * 得到该节点下指定的节点 55 | * @param: @param name 56 | * @param: @return 57 | * @return: Element 58 | * @Autor: Han 59 | */ 60 | public static Element getElement(Element element, String name) { 61 | Element childElement = element.element(name); 62 | if(childElement == null) { 63 | logger.error(element.getName() + "节点下没有子节点" + name); 64 | return null; 65 | } 66 | return childElement; 67 | } 68 | 69 | /** 70 | * 得到该节点的内容 71 | * @param: @param element 72 | * @param: @return 73 | * @return: String 74 | * @Autor: Han 75 | */ 76 | public static String getElementText(Element element) { 77 | return element.getText(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/log4j.properties: -------------------------------------------------------------------------------- 1 | ### \u8BBE\u7F6E### 2 | log4j.rootLogger = debug,stdout,D,E 3 | 4 | ### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ### 5 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender 6 | log4j.appender.stdout.Target = System.out 7 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 8 | log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n 9 | 10 | ### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ### 11 | log4j.appender.D = org.apache.log4j.DailyRollingFileAppender 12 | log4j.appender.D.File = E://logs/log.log 13 | log4j.appender.D.Append = true 14 | log4j.appender.D.Threshold = DEBUG 15 | log4j.appender.D.layout = org.apache.log4j.PatternLayout 16 | log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n 17 | 18 | ### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ### 19 | log4j.appender.E = org.apache.log4j.DailyRollingFileAppender 20 | log4j.appender.E.File =E://logs/error.log 21 | log4j.appender.E.Append = true 22 | log4j.appender.E.Threshold = ERROR 23 | log4j.appender.E.layout = org.apache.log4j.PatternLayout 24 | log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n -------------------------------------------------------------------------------- /web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.cszjo.com.http.handler.impl.LogionHandler 5 | /login 6 | 7 | --------------------------------------------------------------------------------