├── 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 |
--------------------------------------------------------------------------------