理解与模拟一个简单web服务器

 

  先简单说下几个概念,根据自己的理解,不正确请见谅。

web服务器

     首先要知道什么是web服务器,简单说web服务器就是可以使用HTTP传输协议与客户端进行通信的服务器。最初的web服务器只能用来处理静态页面,而tomcat服务器更加强大,不仅可以处理静态页面,还可以运行java类文件,动态创建页面并返回给浏览器,之所以说tomcat是个容器,就是可以运行servle类的容器,同时可以处理http请求,并返回相应内容。

HTTP协议

    http协议是应用层协议,底层使用tcp建立可靠传输连接。所谓协议就是规定了传输内容的规范或格式。先看一个浏览器的http请求:   

  


1 POST /inner/index.html HTTP/1.1
2 Host: localhost:8080
3 Connection: keep-alive
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
5 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0
6 Accept-Encoding: gzip,deflate,sdch
7 Accept-Language: zh-CN,zh;q=0.8
8 
9 name=ligen&age=21

  

 

    http请求包括三部分:

  1-7行可以归为请求头部分,然后8是空行用以区分实体部分,9行是请求实体内容

  1. (代码1)请求方法--uri--协议/版本
  2. (代码2-7)请求头
  3. (代码9)实体

  第一行POST /inner/index.html HTTP/1.1,POST就是请求方法(还有get等请参考其它),然后是空格;/inner/index.html是请求的资源,一般是相对于根路径的,所以总是以“/”开头;最后是协议和版本。

  http的响应格式与请求类似:

  

1 HTTP/1.1 200 OK
2 server:tomcat
3 Content-Type:text/html
4 Content-Length:100
5 
6 <html>
7 ......
8 </html>

  第一行指定了协议和版本,接着是状态码200,服务器一切运行正常,与请求类似,响应实体部分与信息头部分通过一个空行分隔。

开始建立web服务器

  现在开始着手建立一个简单的web服务器,需要大家有socket基础(了解即可),服务器的基本功能就是基于客户端的请求,建立请求对象request,然后处理相应逻辑找到资源,并封装成response对象通过http将数据传回客户端。因为需要建立三个类:

  • HttpServer 使用socket负责建立服务器,等待客户端连接,建立连接后,根据请求信息初始化Request、Response
  • Request 获取socket的InputStream,封装了客户端的请求信息
  • Response 对应Request的返回信息,获取socket的OutputStream处理返回数据 

(一)HttpServer类

  HttpServer主要建立本地8080端口服务器,等待连接请求,建立请求后使用socket的InputStream和OutputStream分别初始化Request和Response对象。

 1 public class HttpServer {
 2     pupublic void await() {
 3         ServerSocket serverSocket = null;
 4         int port = 8080;
 5         try {
 6             serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
 7         } catch (IOException e) {
 8             e.printStackTrace();
 9             System.exit(1);
10         }
11         while (true){
12             Socket socket = null;
13             InputStream inputStream = null;
14             OutputStream outputStream = null;
15             try {
16                 socket = serverSocket.accept();
17                 inputStream = socket.getInputStream();
18                 outputStream = socket.getOutputStream();
19                 Request request = new Request(inputStream);//使用连接的inputStream初始化Request
20                 request.Parse();//Parse用于解析原始HTTP请求数据
21                 Response response = new Response(outputStream);
22                 response.setRequest(request);
23                 response.sendStaticResource();
24                 socket.close();
25             } catch (IOException e) {
26                 e.printStackTrace();
27             }
28         }
29     }
30 }    

 (二)Request类

 

 1 public class Request{
 2     
 3     private String uri;
 4     private InputStream in;
 5     
 6     public Request(InputStream in){
 7         this.in = in;
 8     }
 9     //根据请求头信息获取uri,如GET /index.html HTTP/1.1 ,可以返回/index.html
10     public String parseUri(String request){
11         int index1,index2;
12         index1 = request.indexOf(" ");
13         if (index1 != -1){
14             index2 = request.indexOf(" ", index1+ 1);
15             if (index1 < index2)
16                 return request.substring(index1 + 1, index2);
17         }
18         return null;
19     }
20     //根据socket的InputStream读取整个字节流,存储在字节数组中,然后使用内存中的字节数组构建StringBUffer对象
21     public void Parse(){
22         StringBuffer request = new StringBuffer(2048);
23         byte[] bs = new byte[2048];
24         int i;
25         try {
26             i = in.read(bs);
27         } catch (IOException e) {
28             e.printStackTrace();
29             i = -1;
30         }
31         
32         for(int j=0; j<i; j++){
33             request.append((char)bs[j]);
34         }
35         System.out.println(request.toString());
36         uri = parseUri(request.toString());
37     }
38     
39     public String getUri() {
40         return uri;
41     }
42 }

 (三)Response类

  Response使用socket返回的OutputStream构建对象,setRequest()方法使用初始化后的Request对象出给Response,sendStaticResource()用于发送静态资源,如html文件。

 1 public class Response implements ServletResponse{
 2     
 3     private static final int BUFFER_SIZE = 1024;
 4     private Request request;
 5     private OutputStream out;
 6     private PrintWriter writer;
 7     
 8     public  Response(OutputStream out) {
 9         this.out = out;
10     }
11     
12     public  void setRequest(Request request) {
13         this.request = request;
14     }
15     
16     public void sendStaticResource() throws IOException{
17         byte[] buffer = new byte[BUFFER_SIZE];
18         FileInputStream fis = null;
19         File file = new File(Constant.WEB_ROOT, request.getUri());
20         try {
21             fis = new FileInputStream(file);
22             int ch = fis.read(buffer, 0, BUFFER_SIZE);
23             while (ch != -1){
24                 out.write(buffer, 0, BUFFER_SIZE);
25                 ch = fis.read(buffer, 0, BUFFER_SIZE);
26             }
27         } catch (FileNotFoundException e) {//当文件不存在时,返回错误信息
28             String html = "<h1>file not found</h1>";
29             String errorMsg = "HTTP/1.1 404 file not found\r\n" + 
30                     "Content-Type: text/html\r\n" + 
31                     "Content-Length: " + html.getBytes().length + "\r\n" +
32                     "\r\n" +
33                     html;
34             out.write(errorMsg.getBytes());
35         }
36         finally {
37             if(fis != null){
38                 fis.close();
39             }
40         }
41     }
42 }

  (四)创建MyServer类,启动服务器

1 public class MyServer {
2     public static void main(String[] args) {
3         HttpServer server = new HttpServer();
4         server.await();
5     }
6 }

 

posted @ 2016-11-21 18:14  music180  阅读(750)  评论(0编辑  收藏  举报