java nio手动实现简单的http服务器

   需求分析

   最近在学习HTTP协议,还是希望动手去做一做,所以就自己实现了一个http服务器,主要功能是将http请求封装httpRequest,通过解析web.xml,用不同的handler处理不同的uri,然后再将封装好的httpResponse还原成http响应返回浏览器。

  代码已经成功上传至  GitHub

如果对你学习JavaNIO有帮助的话,记得给个star哦!

https://github.com/hansiming/HttpServerByJavaNIO

  代码

  使用java nio实现监听,完成服务器监听线程

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上下文抽象类设计

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,成功!

计算机网络(13)-----java nio手动实现简单的http服务器的更多相关文章

  1. JAVA NIO non-blocking模式实现高并发服务器

    JAVA NIO non-blocking模式实现高并发服务器 分类: JAVA NIO2014-04-14 11:12 1912人阅读 评论(0) 收藏 举报 目录(?)[+] Java自1.4以后 ...

  2. JAVA NIO non-blocking模式实现高并发服务器(转)

    原文链接:JAVA NIO non-blocking模式实现高并发服务器 Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要 ...

  3. java nio 写一个完整的http服务器 支持文件上传 chunk传输 gzip 压缩 使用过程 和servlet差不多

    java nio 写一个完整的http服务器  支持文件上传   chunk传输    gzip 压缩      也仿照着 netty处理了NIO的空轮询BUG        本项目并不复杂 代码不多 ...

  4. Java nio Client端简单示例

    java nio是一种基于Channel.Selector.Buffer的技术,它是一种非阻塞的IO实现方式 以下Client端示例 public class ClientNio { public s ...

  5. 手动实现简单的tomcat服务器

    手动实现tomcat服务器的流程: 分析具体的实现步骤: 1,浏览器和后端服务如何实现通信,首先传输的数据要遵循http协议,通过tcp也就是我们常说的套接字编程来实现,具体的底层数据传输肯定就是我们 ...

  6. Java NIO之选择器

    1.简介 前面的文章说了缓冲区,说了通道,本文就来说说 NIO 中另一个重要的实现,即选择器 Selector.在更早的文章中,我简述了几种 IO 模型.如果大家看过之前的文章,并动手写过代码的话.再 ...

  7. Java NIO 完全学习笔记(转)

    本篇博客依照 Java NIO Tutorial翻译,算是学习 Java NIO 的一个读书笔记.建议大家可以去阅读原文,相信你肯定会受益良多. 1. Java NIO Tutorial Java N ...

  8. Java NIO 学习总结 学习手册

    原文 并发编程网(翻译):http://ifeve.com/java-nio-all/  源自 http://tutorials.jenkov.com/java-nio/index.html Java ...

  9. Java NIO案例

    Java 网络IO编程总结(BIO.NIO.AIO均含完整实例代码)   http://blog.csdn.net/anxpp/article/details/51512200 Java NIO框架N ...

随机推荐

  1. GOLANG 基本数据类型 整型

    基本数据类型-整型 种类     有符号(负号)      int8 int16 int32 int64 无符号(无符号) uint8 uint16 uint32 uint64 架构特定(取决于系统位 ...

  2. 谷歌input框黄色背景问题

    input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,input:-webkit-autof ...

  3. 测试oracle数据库的脱机备份和恢复

    环境:windows7.Oracle11g 一.脱机备份 脱机备份是指在数据库关闭情况下的数据备份,也称为冷备份. 在书上学到的备份步骤: 1.记录所要备份数据库文件所在的操作系统路径: 2.关闭数据 ...

  4. 关于margin的一些问题

    引 在平时处理样式的过程中,会出现各种问题.比如: 包含在父元素中的子元素设置了浮动,子元素高度变化的时候父元素的高度没有随着变化,就是没有被撑高,父元素仍然是原来设置的那个高度 包含在父元素中的子元 ...

  5. python成长之路【第十三篇】:Python操作MySQL之pymysql

    对于Python操作MySQL主要使用两种方式: 原生模块 pymsql ORM框架 SQLAchemy pymsql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎 ...

  6. html中css、div命名规范

    html中css.div命名规范 1.类class的命名规范示例 头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:column ...

  7. PBOC金融IC卡,卡片与终端交互的13个步骤,简介-第四组(转)

    十:联机处理-可选项终端根据卡片行为分析的结果,执行对应的处理.若卡片响应联机,则终端发起联机操作.联机处理使得发卡行后台可以根据基于后台的风险管理参数检查并授权批准或拒绝交易.除了传统的联机欺诈和信 ...

  8. PBOC金融IC卡,卡片与终端交互的13个步骤,简介-第一组(转)

    两个PPT结合起来--一些基础介绍--每一步的详细细节还要去研读文档 EMV-全球标准PBOC-国内标准 ----------------------一:必选:应用选择应用选择的方法:目录选择法.AI ...

  9. iOS使用Security.framework进行RSA 加密解密签名和验证签名

    iOS 上 Security.framework为我们提供了安全方面相关的api: Security框架提供的RSA在iOS上使用的一些小结 支持的RSA keySize 大小有:512,768,10 ...

  10. 解析json串,利用正则表达式,split

    public class SplitJson { public static void main(String[] args) {        // TODO Auto-generated meth ...