手写简单的HttpServer基于Java nio 实现socket异步通信(请求映射注解方式)

HttpServer服务类

 1 package javax.servlet.http.server2;
 2 
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.channels.SelectionKey;
 6 import java.nio.channels.Selector;
 7 import java.nio.channels.ServerSocketChannel;
 8 import java.util.Iterator;
 9 
10 public class HttpServer {
11     private int DEFULT_PORT=8080;               //默认端口
12     private boolean isShudown=true;            //服务状态
13     private ServerSocketChannel ssc;
14     public HttpServer(){
15         try{
16             start(DEFULT_PORT);
17         } catch (IOException e){
18             e.printStackTrace();
19         }
20     }
21     
22     public HttpServer(int port){
23         try{
24             start(port);
25         } catch (IOException e){
26             e.printStackTrace();
27         }
28     }
29     public void start(int port) throws IOException{
30         ssc=ServerSocketChannel.open(); // 打开服务器套接字通道
31         ssc.socket().bind(new InetSocketAddress(port));     //绑定到特定端口
32         ssc.configureBlocking(false);                      //设置为非阻塞模式
33         Selector selector=Selector.open();                 //打开一个选择器
34         ssc.register(selector, SelectionKey.OP_ACCEPT);    //向给定的选择器注册此通道,返回一个选择键
35         while(isShudown){
36             /*if(selector.select(3000)==0){         //没有请求阻塞3秒,继续执行
37                 continue;
38             }*/
39             if(selector.select()>0){        //等待请求,请求过来继续执行,没有请求一直等待
40                 Iterator keyIter=selector.selectedKeys().iterator();          //获取等待处理的请求
41                 while (keyIter.hasNext()){
42                     SelectionKey key=keyIter.next();
43                     new Thread(new HttpHandler(key)).run();                    // 启动新线程处理SelectionKey
44                     keyIter.remove();                                           // 处理完后,从待处理的SelectionKey迭代器中移除当前所使用的key
45                }
46             }
47              
48         } 
49     }
50     
51     /**
52      * 关闭端口
53      * @throws IOException
54      */
55     public void stop() throws IOException{
56         isShudown=false;
57         //ssc.socket().close();
58     }
59     
60     /**
61      * 打开服务的主入口
62      * @param args
63      * @throws Exception
64      */
65     public static void main(String[] args) throws Exception{
66 
67         new HttpServer(2222);
68     }
69 
70     
71 }

HttpHandler请求处理类

 1 package javax.servlet.http.server2;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 import java.io.StringWriter;
 6 import java.nio.ByteBuffer;
 7 import java.nio.channels.SelectionKey;
 8 import java.nio.channels.ServerSocketChannel;
 9 import java.nio.channels.SocketChannel;
10 
11 public class HttpHandler implements Runnable{
12     private int bufferSize = 1024;
13     private SelectionKey key;
14     private int CODE=200;
15     public HttpHandler(SelectionKey key){
16         this.key = key;
17     }
18 
19     /**
20      * 接收连接处理
21      * @throws IOException
22      */
23     public void handleAccept() throws IOException {
24         SocketChannel clientChannel=((ServerSocketChannel)key.channel()).accept();
25         clientChannel.configureBlocking(false);     //线程不阻塞
26         clientChannel.register(key.selector(), SelectionKey.OP_READ|SelectionKey.OP_WRITE, ByteBuffer.allocate(bufferSize));       //注册读写
27         
28     }
29     @Override
30     public void run()
31     {
32         // 接收到连接请求时
33         if (key.isAcceptable())
34         {
35             try
36             {
37                 handleAccept();
38             } catch (IOException e)
39             {
40                 e.printStackTrace();
41             }
42 
43         }
44         
45         if (key.isReadable()&&key.isWritable())   //可读可写
46         {
47             try
48             {
49                 HttpServletRequest ser=new HttpServletRequest(key);                //封装Request
50                 HttpServletResponse serr=new HttpServletResponse(key);             //封装Response
51                 HttpServlet servlet=WebApp.getServlet(ser.getUrl());               //通过映射地址获取具体的servlet
52                 if(servlet==null){
53                     this.CODE=404;
54                 }else{
55                     try
56                     {
57                         servlet.service(ser, serr);
58                     } catch (Exception e)
59                     {
60                         e.printStackTrace();
61                         this.CODE=500;
62                         StringWriter sw = new StringWriter();   
63                         PrintWriter pw = new PrintWriter(sw, true);   
64                         pw.flush();   
65                         sw.flush(); 
66                         serr.println(sw.toString());
67                     }
68                 }
69                 serr.pushToClient(CODE);
70                 ser.close();
71                 serr.close();
72             } catch (IOException e)
73             {
74                 e.printStackTrace();
75             }
76             
77         }
78         
79         
80     }
81 }

HttpServlet基类

 1 package javax.servlet.http.server2;
 2 
 3 public abstract class HttpServlet {
 4     public void service(HttpServletRequest req,HttpServletResponse rep) throws Exception{
 5         this.doGet(req,rep);
 6     }
 7     
 8     protected  abstract void doGet(HttpServletRequest req,HttpServletResponse rep) throws Exception;
 9     protected  abstract void doPost(HttpServletRequest req,HttpServletResponse rep) throws Exception;
10 }

 

HttpServletRequest请求封装类

  1 package javax.servlet.http.server2;
  2 
  3 import java.io.IOException;
  4 import java.nio.ByteBuffer;
  5 import java.nio.channels.SelectionKey;
  6 import java.nio.channels.SocketChannel;
  7 import java.nio.charset.Charset;
  8 import java.util.ArrayList;
  9 import java.util.Arrays;
 10 import java.util.HashMap;
 11 import java.util.List;
 12 import java.util.Map;
 13 import java.util.StringTokenizer;
 14 
 15 public class HttpServletRequest
 16 {
 17     private final String DEFULT_CODE="UTF-8";
 18     private final String CRLF="\r\n";      //回车换行
 19     private final String SPACE=" ";
 20     private SocketChannel sc;
 21     private ByteBuffer buffer;
 22     private String requestInfo;
 23     private String method;
 24     private String url;
 25     private Map> parameterMapValues;
 26     
 27     public HttpServletRequest(){
 28         parameterMapValues=new HashMap>();
 29     }
 30     public HttpServletRequest(SelectionKey key) throws IOException{
 31         this();
 32         sc=(SocketChannel)key.channel();
 33         buffer=(ByteBuffer)key.attachment();
 34         buffer.clear();
 35         if(sc.read(buffer)==-1){
 36              sc.close();
 37         }else{
 38              buffer.flip();
 39              requestInfo=Charset.forName(DEFULT_CODE).newDecoder().decode(buffer).toString();
 40         }
 41          parseRequestInfo();
 42     }
 43     
 44     /**
 45      * 分析请求,主要是获取资源地址、封装专递的参数
 46      */
 47     private void parseRequestInfo(){
 48         String paramInfo = null;
 49         if(requestInfo==null||"".equals(requestInfo)){
 50             return ;
 51         }
 52         String[] requestMessage = requestInfo.split(CRLF);        //得到请求的消息
 53         String[] firstLine = requestMessage[0].split(SPACE);      //获取首行头文件
 54         this.method=firstLine[0];                  //获取提交方式
 55         String tempUrl=firstLine[1];               //获取资源地址
 56         if(method.equals(RequestType.POST.toString())){
 57             this.url=tempUrl;
 58             paramInfo=requestMessage[requestMessage.length-1];
 59         }else if(method.equals(RequestType.GET.toString())){
 60             if(tempUrl.contains("?")){       //如果有参数
 61                 String params[]=tempUrl.split("\\?");
 62                 this.url=params[0];
 63                 paramInfo=params[1];//接收请求参数
 64             }else
 65                 this.url=tempUrl;
 66         }
 67         
 68         if(paramInfo==null||"".equals(paramInfo)){
 69             return ;
 70         }else
 71             parseParams(paramInfo);
 72     }
 73 
 74     /**
 75      * 保存传递的参数
 76      * @param paramInfo
 77      */
 78     private void parseParams(String paramInfo){
 79         StringTokenizer token=new StringTokenizer(paramInfo,"&");
 80         while(token.hasMoreTokens()){
 81             String keyValue =token.nextToken();
 82             String[] keyValues=keyValue.split("=");
 83             if(keyValues.length==1){
 84                 keyValues =Arrays.copyOf(keyValues, 2);
 85                 keyValues[1] =null;
 86             }
 87             
 88             String key = keyValues[0].trim();
 89             String value = null==keyValues[1]?null:keyValues[1].trim();
 90             //转换成Map 分拣
 91             if(!parameterMapValues.containsKey(key)){
 92                 parameterMapValues.put(key,new ArrayList());
 93             }
 94             
 95             List values =parameterMapValues.get(key);
 96             values.add(value);            
 97         }        
 98     }
 99     
100     /**
101      * 根据页面的name 获取对应的多个值
102      * @param name   名
103      */
104     public String[] getParameterValues(String name){
105         List values=null;
106         if((values=parameterMapValues.get(name))==null){
107             return null;
108         }else{
109             return values.toArray(new String[]{});
110         }
111     }
112     
113     /**
114      * 返回单个值
115      * @param name 名
116      * @return
117      */
118     public String getParameter(String name){
119         String[] values =getParameterValues(name);
120         if(null==values){
121             return null;
122         }
123         return values[0];
124     }
125     
126     /**
127      * 获取请求方法
128      * @return
129      */
130     public String getMethod()
131     {
132         return method;
133     }
134     
135     /**
136      * 获取url资源访问地址
137      * @return
138      */
139     public String getUrl()
140     {
141         return url;
142     }
143     /**
144      * 关闭连接
145      */
146      void close()
147     {
148         try
149         {
150             if (sc.isOpen())
151             {
152                 sc.close();
153             }
154         } catch (IOException e)
155         {
156             e.printStackTrace();
157         }
158     }
159 }

HttpServletResponse响应封装类

  1 package javax.servlet.http.server2;
  2 
  3 import java.io.IOException;
  4 import java.nio.ByteBuffer;
  5 import java.nio.channels.SelectionKey;
  6 import java.nio.channels.SocketChannel;
  7 import java.util.Date;
  8 public class HttpServletResponse
  9 {
 10 
 11     //两个常量
 12     public static final String CRLF="\r\n";
 13     public static final String BLANK=" ";
 14     private static final String localCharset="UTF-8";
 15     //正文
 16     private StringBuilder content;
 17     //存储头信息
 18     private StringBuilder headInfo;
 19     //存储正文长度
 20     private int len =0;
 21     private StringBuilder bu;
 22     private ByteBuffer buffer;
 23     private SocketChannel sc;
 24     public HttpServletResponse(){
 25         bu=new StringBuilder();
 26         headInfo =new StringBuilder();
 27         content =new StringBuilder();
 28         len =0;
 29     }
 30     public HttpServletResponse(SelectionKey key){
 31         this();
 32         sc=(SocketChannel)key.channel();
 33         buffer=(ByteBuffer)key.attachment();
 34         buffer.clear();
 35         buffer.flip();
 36     }
 37     
 38     /**
 39      * 构建正文
 40      */
 41     public HttpServletResponse print(String info){
 42         content.append(info);
 43         len+=info.getBytes().length;
 44         return this;
 45     }
 46     
 47     /**
 48      * 构建正文+回车
 49      */
 50     public HttpServletResponse println(String info){
 51         content.append(info).append(CRLF);
 52         len+=(info+CRLF).getBytes().length;
 53         return this;
 54     }
 55     
 56     /**
 57      * 构建响应头
 58      */
 59     private void createHeadInfo(int code){
 60         //1)  HTTP协议版本、状态代码、描述
 61         headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
 62         switch(code){
 63             case 200:
 64                 headInfo.append("OK");
 65                 break;
 66             case 404:
 67                 headInfo.append("NOT FOUND");
 68                 break;
 69             case 505:
 70                 headInfo.append("SEVER ERROR");
 71                 break;    
 72         }
 73         headInfo.append(CRLF);
 74         //2)  响应头(Response Head)
 75         headInfo.append("Server:bjsxt Server/0.0.1").append(CRLF);
 76         headInfo.append("Date:").append(new Date()).append(CRLF);
 77         headInfo.append("Content-type:text/html;charset="+localCharset).append(CRLF);
 78         //正文长度 :字节长度
 79         headInfo.append("Content-Length:").append(len).append(CRLF);
 80         headInfo.append(CRLF); //分隔符
 81     } 
 82     
 83     //推送到客户端
 84     void pushToClient(int code) throws IOException{
 85             if(null==headInfo){
 86                 code =500;
 87             }
 88             createHeadInfo(code);
 89             //头信息+分割符
 90             bu.append(headInfo.toString());
 91             
 92             //正文
 93             bu.append(content.toString());
 94             buffer = ByteBuffer.wrap(bu.toString().getBytes(localCharset));
 95             if(sc.isOpen())
 96                 sc.write(buffer);
 97             
 98         }
 99     
100     /**
101      * 关闭
102      */
103     void close(){
104         try
105         {
106             if(sc.isOpen()){
107                 sc.close();
108             }
109             
110         } catch (IOException e)
111         {
112             e.printStackTrace();
113         }
114     }
115 }

 

RequestType请求类型

1 package javax.servlet.http.server2;
2 
3 public enum RequestType
4 {
5 POST    //post请求
6 ,GET    //get请求
7 
8 }

ServletContext 容器

 1 package javax.servlet.http.server2;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 
 7 public class ServletContext
 8 {
 9 
10     private Mapservlet;
11     public ServletContext(){
12         servlet=new HashMap();
13     }
14     
15     /**
16      * 获取servlet容器
17      * @return
18      */
19     public Map getServlet()
20     {
21         return servlet;
22     }
23     
24 }

 

WebApp功能映射处理

 1 package javax.servlet.http.server2;
 2 
 3 import java.util.Iterator;
 4 import java.util.Map;
 5 import javax.servlet.annotation.WebServlet;
 6 import javax.servlet.util.PackageScanUtils;
 7 
 8 public class WebApp
 9 {
10 
11     private static ServletContext context;
12     static{
13         context=new ServletContext();
14         Mapservlet=context.getServlet();
15         Iterator>clas=PackageScanUtils.getClasses("javax.servlet.http.server2",true).iterator();
16         while(clas.hasNext()){
17             Classcla=clas.next();
18             WebServlet ws=cla.getAnnotation(WebServlet.class);
19             if(ws!=null){
20                 servlet.put(ws.value(), cla.getName());
21             }
22         }
23     }
24     
25     /**
26      * 获取url地址
27      * @param url
28      * @return
29      */
30     public static HttpServlet getServlet(String url)
31     {
32         Maptemp=context.getServlet();
33         if(temp.containsKey(url)){
34             try
35             {
36                 return (HttpServlet)Class.forName(temp.get(url)).newInstance();
37             } catch (InstantiationException e)
38             {
39                 e.printStackTrace();
40             } catch (IllegalAccessException e)
41             {
42                 e.printStackTrace();
43             } catch (ClassNotFoundException e)
44             {
45                 e.printStackTrace();
46             }
47         }
48         return null;
49     }
50 }

Message 状态

 1 package javax.servlet.cod;
 2 
 3 public class Message
 4 {
 5 
 6     public static String message404(){
 7         StringBuilder conetxt=new StringBuilder();
 8         conetxt.append("lishenglin - Error report 

HTTP Status 404 -


type Status"); 16 conetxt.append("report

message

description The requested resource is not available.


); 17 conetxt.append("noshade=\"noshade\">

lishenglin

"); 18 return conetxt.toString(); 19 20 } 21 22 }

PackageScanUtils 扫描class

  1 package javax.servlet.util;
  2 
  3 import java.io.File;
  4 import java.io.FileFilter;
  5 import java.io.FileNotFoundException;
  6 import java.io.IOException;
  7 import java.net.URL;
  8 import java.net.URLDecoder;
  9 import java.util.Enumeration;
 10 import java.util.Iterator;
 11 import java.util.LinkedHashSet;
 12 import java.util.Set;
 13 
 14 public class PackageScanUtils
 15 {
 16     
 17         /**
 18          * 获取class集合
 19          * @param packName         包名
 20          * @param scanSubdirectory  扫描子目录
 21          * @return
 22          */
 23     public static Set> getClasses(String packName,boolean scanSubdirectory)
 24     {
 25 
 26         Set> classes = new LinkedHashSet>(); // 装载class集合
 27         String packageName = packName; // 获取包的名字 并进行替换
 28         String packageUrlName = packageName.replace('.', '/');
 29         Enumeration urls; // 定义一个枚举的集合 并进行循环来处理这个目录下的class
 30         try
 31         {
 32             urls = Thread.currentThread().getContextClassLoader().getResources(packageUrlName);
 33 
 34             // 循环迭代下去
 35             while (urls.hasMoreElements())
 36             {
 37 
 38                 URL url = urls.nextElement(); // 获取下一个元素
 39 
 40                 String protocol = url.getProtocol(); // 得到协议的名称,比如http、file
 41 
 42                 if ("file".equals(protocol))
 43                 { // 如果是以文件的形式保存在服务器上
 44 
 45                     String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); // 获取包的物理路径
 46                     // 以文件的方式扫描整个包下的文件 并添加到集合中
 47                     findClassesInPackageByFile(packageName, filePath, scanSubdirectory, classes);
 48                 }
 49             }
 50         } catch (IOException e)
 51         {
 52             e.printStackTrace();
 53          }
 54 
 55         return classes;
 56     }
 57     
 58     /**
 59      * 以文件的方式扫描整个包下的文件 并添加到集合中
 60      * @param packageName   包名
 61      * @param packagePath   包路径
 62      * @param scanSubdirectory     是否扫描子目录
 63      * @param classes        class集合
 64      * @throws FileNotFoundException 
 65      */
 66     public static void findClassesInPackageByFile(String packageName, String packagePath, final boolean scanSubdirectory, Set> classes) throws FileNotFoundException
 67     {
 68 
 69         File dir = new File(packagePath); // 获取此包的目录 建立一个File
 70         File[] dirfiles = dir.listFiles(new FileFilter()
 71         { // 如果存在 就获取包下的所有文件 包括目录 
 72 
 73             public boolean accept(File file)
 74             { // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
 75                 return (scanSubdirectory && file.isDirectory()) || (file.getName().endsWith(".class"));
 76             }
 77         });
 78 
 79         for (File file : dirfiles)
 80         { // 循环所有文件
 81             
 82             if (file.isDirectory())
 83             { // 如果是目录 则继续扫描
 84                 findClassesInPackageByFile(packageName==""? file.getName():packageName + "." + file.getName(), file.getAbsolutePath(), scanSubdirectory, classes);
 85             } else
 86             { // 如果是java类文件 去掉后面的.class 只留下类名
 87 
 88                 String className = file.getName().substring(0, file.getName().length() - 6);
 89                 try
 90                 {
 91                     if("".equals(packageName)){
 92                         classes.add(Thread.currentThread().getContextClassLoader().loadClass(className));
 93                     }else{
 94                         classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
 95                     }
 96                 } catch (ClassNotFoundException e)
 97                 {
 98                     e.printStackTrace();
 99                 }
100             }
101         }
102     }
103     
104     public static void main(String[] args)
105     {
106         Set> clas=PackageScanUtils.getClasses("cn.javax",true);
107         Iterator>cla=clas.iterator();
108         while(cla.hasNext()){
109             /*Annotation[]ann=cla.next().getAnnotations();
110             for(Annotation a:ann){
111                 if(a instanceof WebServlet){
112                     System.out.println(((WebServlet) a).value());
113                 }
114             }*/
115             System.out.println(cla.next().getName());
116         }
117     }
118 }

 

MyServlet 测试servlet

 1 package javax.servlet.http.server2;
 2 
 3 import javax.servlet.annotation.WebServlet;
 4 
 5 @WebServlet("/myservlet")
 6 public class Myservlet extends HttpServlet
 7 {
 8     
 9 
10     @Override
11     protected void doGet(HttpServletRequest req, HttpServletResponse rep) throws Exception
12     {
13         this.doPost(req, rep);
14         
15     }
16 
17     @Override
18     protected void doPost(HttpServletRequest req, HttpServletResponse rep) throws Exception
19     {
20         String name=req.getParameter("name");
21         rep.println("获取的参数name="+name);
22         rep.println("请求的url是:"+req.getUrl());
23         
24     }
25 }

WebServlet简单注解

 1 package javax.servlet.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 @Target(ElementType.TYPE)
 9 @Retention(RetentionPolicy.RUNTIME)
10 /**
11  * webServlet注解
12  * @author 李圣霖
13  * @date 2017年3月28日
14  */
15 public abstract @interface WebServlet
16 {
17     public abstract java.lang.String value() default "";        //映射地址
18 }

 

测试

手写简单的HttpServer基于Java nio 实现socket异步通信(请求映射注解方式)_第1张图片

 

转载于:https://www.cnblogs.com/xysl/p/6656909.html

你可能感兴趣的:(手写简单的HttpServer基于Java nio 实现socket异步通信(请求映射注解方式))