java 手动写http静态网页服务器

本文仅仅是实现一个功能非常有限的http服务器。我仅仅实现了请求一个html和请求一个jpg图片。方式只支持GET。不支持http选项。错误代码仅仅会返回200 400 404.支持xml对服务器的配置。本博客内容仅仅完全处于自身娱乐,高手可直接略过。

实现思想

1用java的sax解析服务器配置文件。确定web服务器的root目录,和web服务器运行的端口号。
2启动一个serverSocket等待链接
3获取一个链接之后把所获得的socket传递给新的httpSolver线程。httpSolver负责解析客户端发来的http请求头。
4如果httpSolver根据服务器请求的文件建立一个HttpMessage类。这个类封装了http请求的消息,包括请求的文件,root目录等等。这个httpMessage传递给GetDisk类
5GetDisk负责从硬盘读取。如果java发现请求的文件不在,那么在返回404.如果请求文件成功那么构造消息头返回200在返回锁请求文件。
6httpSolver关闭socket结束一个回话。

工程文件管理方式

这里写图片描述

xml配置文件

server.xml

?
1
2
3
4
5
6
<code class = "language-xml" hljs= "" ><!--?xml version= 1.0 encoding=UTF- 8 ?-->
 
<server>
     <port> 80 </port>
     <root>d:Root</root>
</server></code>

server.dtd

?
1
2
3
4
5
<code class = "language-xml" hljs= "" ><!--?xml version= 1.0 encoding=UTF- 8 ?-->
 
<!--ELEMENT server (port,root?)-->
<!--ELEMENT port (#PCDATA)-->
<!--ELEMENT root (#PCDATA)--></code>

Main.java

Main.java

?
1
2
3
4
5
6
7
8
9
<code class = "language-java" hljs= "" > package httpServer;
 
public class Main {
     public static void main(String args[])
     {
         ServerClass myServer= new ServerClass();
         myServer.serverStart();
     }
}</code>

ServerClass.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<code class = "language-java" hljs= "" > public class ServerClass {
     private ServerSocket serverSocket;
     private String root;
     private boolean runing;
     private int port;
     public ServerClass()
     {
         runing= false ;
         SAXParserFactory saxpf=SAXParserFactory.newInstance();
         SAXParser saxParser;
 
         try (InputStream in= new FileInputStream(server.xml))
         {
             saxParser=saxpf.newSAXParser();
             saxParser.parse(in, new Handler());        
         }
         catch (FileNotFoundException e) {
             // TODO Auto-generated catch block
             System.out.println(server.xml no find);
             e.printStackTrace();
 
         }
         catch (Exception e) {
             // TODO Auto-generated catch block
             System.out.println(parse server.xml error);
             e.printStackTrace();
 
         }
 
     }
 
 
     public void serverStart()
     {
         try {
             serverSocket= new ServerSocket(port);
         } catch (IOException e) {
             // TODO Auto-generated catch block
             System.out.println(ServerSocket cann't establish);
             e.printStackTrace();
         }
         runing= true ;
         System.out.println(System is runing);
         while (runing)
         {
             try {
                 Socket incomingSocket=serverSocket.accept();
 
 
                 new Thread( new requestHandler(incomingSocket,root)).start();
 
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
             System.out.println(System deal one request);
         }
         System.out.println(System is down);
     }
 
     public void shutDown()
     {
         runing= false ;
     }
 
     private class Handler extends DefaultHandler
     {
             //单独介绍
     }
}</code>

上面的代码就是调用xml解析工具对服务器的一些基本的内容进行设置。因为我们只对配置文件进行读取操作,所以我们可以简单的使用sax作为简单的解析工具.不用使用dom.之后创建一个serversocket等待链接。
在serverclass里面有一个私有的内部嵌套类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<code class = "language-java" hljs= "" > private class Handler extends DefaultHandler
     {
         private boolean isPort;
         private boolean isRoot;
         public Handler()
         {
             super ();
             isPort= false ;
             isRoot= false ;
 
         }
 
 
         @Override
         public void characters( char [] ch, int start, int length)
                 throws SAXException {
 
             super .characters(ch, start, length);
             String temp= new String(ch,start,length);
 
             if (isPort)
             {
 
                 port=Integer.parseInt(temp);
                 isPort= false ;
             }
             else if (isRoot)
             {
 
                 root=temp;
                 isRoot= false ;
             }
         }
 
 
         @Override
         public void startElement(String uri, String localName, String qName,
                 Attributes attributes) throws SAXException {
 
             super .startElement(uri, localName, qName, attributes);
             if (qName.equals(port))
             {
                 isPort= true ;
             }
             else if (qName.equals(root))
             {
                 isRoot= true ;
             }
             else if (qName.equals(server))
             {
                 //不操作
             }
             else
             {
                 System.out.println(error xml element);
             }
 
         }
</code>

这个类是用于处理sax解析过程的.因为sax在解析的时候是事件回调的方式.我们要重载DefaultHandler里面的函数.sax在解析的过程中,如果碰到element那么久调用startElement函数,并且把元素等等的名字作为参数传递到里面.当一个element结束的时候调用endElement函数.DefaultHandler里面的各个函数都是空操作.所以我们要进行重载来实现我们想要的操作.私有变量isroot等等是因为在进入元素的时候sax统一调用startElement,碰到里面的值得时候比如 < root > D: oot < / root >在碰到D: oot 的时候会调用characters。但是我们怎么知道D: oot是给root的?就是设置一个isroot变量。表示现在的在处理root元素。把里面的值给root。因为其他的元素比如< port > 80 < / port >碰到80的时候同样会调用characters。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class = "language-java" hljs= "" >
  class requestHandler implements Runnable
{
     Socket incomingSocket;
     String root;
     public requestHandler(Socket incomingSocket,String root) {
         this .incomingSocket=incomingSocket;
         this .root=root;
     }
     @Override
     public void run() {
         new httpSolver(incomingSocket,root).serve();
     }
 
 
}</code>

这个主要是为了建立一个线程.用于同时处理很多很多请求.

httpSolver.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<code class = "language-java" hljs= "" > public class httpSolver {
     Socket incomingSocket;
     String root;
     public httpSolver(Socket incomingSocket,String root) {
         this .incomingSocket=incomingSocket;
         this .root=root;
     }
 
     public void serve()
     {
 
         Scanner in= null ;
 
         OutputStream out= null ;
         try {
             in= new Scanner(incomingSocket.getInputStream());
//          out=new PrintWriter(incomingSocket.getOutputStream(),true);
             out=incomingSocket.getOutputStream();
             HttpMessage httpMessage= new HttpMessage();
             httpMessage.root=root;
             while (in.hasNextLine())
             {
                 String input=in.nextLine();
 
                 String splitResult[]= null ;
                 if (input.startsWith(GET)||input.startsWith(get))
                 {
                     splitResult=input.split( new String( ));
 
                     if (!splitResult[ 2 ].equals(HTTP/ 1.1 ))
                     {
                         new badRequest().work(out);
                         incomingSocket.close();
                         break ;
                     }
                     if (splitResult[ 1 ].equals(/))
                     {
                         httpMessage.targetFile=index.html;
                     }
                     else
                     {
                         httpMessage.targetFile=splitResult[ 1 ].substring( 1 ,splitResult[ 1 ].length());
                     }
                 }
                 else
                 {
                     splitResult=input.split( new String(:));
                     switch (splitResult[ 0 ]) {
                     case If-Modified-Since:
                         httpMessage.If_Modified_Since= true ;
                         httpMessage.If_Modified_SinceString=splitResult[ 1 ];
                         break ;
                     case :
                         new GetDisk(httpMessage).work(out);
                         incomingSocket.close();
                         break ;
                     default :
 
                         break ;
 
 
                     }
 
                 }
 
 
             }
 
         }          
         catch (IOException e) {
             // TODO Auto-generated catch block
             System.out.println(socket io exception);
             e.printStackTrace();
         }
         finally
         {
             try
             {
                 if (in!= null )
                 {
                     in.close();
                 }
                 if (out!= null )
                 {
                     out.close();
                 }
 
             }
             catch (Exception e)
             {
                 e.printStackTrace();
             }
         }
 
     }
}
</code>

这里面包括了一主要的读取循环。不断读取客户端发来的信息。通过解析字符串发现客户端请求的是什么。我们把消息封装到一个HttpMessage里面。这样便于消息的传递,可以把客户端传来的信息都放到HttpMessage里面传递给其他的处理类,比如GetDisk用于读入文件。HttpMessage可以理解为C语言里面的结构体

HttpMessage.java

?
1
2
3
4
5
6
7
<code class = "language-java" hljs= "" > //HttpMessage.java
public class HttpMessage {
     public String root;
     public String targetFile;
     public  boolean If_Modified_Since= false ;
     public String If_Modified_SinceString= null ;
}</code>

GetDisk.java

这个类主要处理从磁盘读取文件。并且把应该返回的http头和http内容都拼装起来。其实这里可以使用 原型设计模式 但是我这里只有几个返回形式。所以直接写在了代码里。但这样对代码维护会造成困难。为了便于加快文件的读取速度。读入输出都是使用二进制方式进行。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<code class = "language-java" hljs= "" > public class GetDisk
{
     private HttpMessage httpMessage;
     private StringBuilder result;
     private Path path;
     public GetDisk(HttpMessage httpMessage)
     {
         result= new StringBuilder();
         this .httpMessage=httpMessage;
 
         path=Paths.get( this .httpMessage.root, this .httpMessage.targetFile);
 
     }
 
     public void work(OutputStream out)
     {
 
         if (!Files.exists(path))
         {
             result.append(HTTP/ 1.1 404 Not Found
Connection: close
);
             result.append(
);
 
             try {
                 out.write(result.toString().getBytes());
                 out.flush();
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
             return ;
 
         }
 
 
         byte [] fileContents= null ;
         try {
             fileContents=Files.readAllBytes(path);
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
          if (fileContents!= null )
         {
             result.append(HTTP/ 1.1 200 OK
);
             if (httpMessage.targetFile.endsWith(html))
             {
                 result.append( Content-Type: text/html; charset=utf- 8
);
             }
             else if (httpMessage.targetFile.endsWith(jpg))
             {
                 result.append( Content-Type: image/jpeg
);
             }
             result.append( Keep-Alive: - 1
);
 
 
             result.append(
);
 
             try {
                 out.write(result.toString().getBytes());
                 out.write(fileContents);
                 out.flush();
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
 
         }  
 
 
         return ;
 
     }
}</code>

badRequest.java

主要返回无法解析的指令错误。其实我们可以把每个返回代码的处理过程都实现为一个类。然后这些类都共同的继承一个超类。在超类中实现写好http返回头。然后参数有个个子类给出就是使用模板设计模式。方便代码的管理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<code class = "language-java" hljs= "" > public class badRequest {
     StringBuilder result;
     public badRequest()
     {
         result= new StringBuilder();
     }
     public void work(OutputStream out)
     {
 
             result.append(HTTP/ 1.1 400 bad request
Connection: close
);
             result.append(
);
 
             try {
                 out.write(result.toString().getBytes());
                 out.flush();
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
             return ;
 
 
     }
}</code>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值