• Tomcat学习笔记(一)一个简单的Web服务器


    内容为《深入剖析Tomcat》第一章重点,以及自己的总结,如有描述不清的,可查看原书。
    一、HTTP协议:
    1、定义:用于服务器与客户端的通讯的协议,允许web服务器和浏览器通过互联网进行发送和接收数据。是一种请求和响应协议,使用可靠的TCP协议,TCP协议的端口为80,是一种面向连接的协议。
    2、HTTP协议请求的三个组成部分:这三部分之间用回车换行符(CRLF)隔开
         请求部分:方法(GET/POST等7种,其他的很少用,书上有介绍)[空格,该部分内容以空格隔开] 统一资源标识符URI[空格,该部分内容以空格隔开 ] 协议/协议版本
         URL通常都是相对服务器的根目录,因此以“/”开头。
         请求头部:请求的头部包含了关于客户端环境和请求的主体内容的有用信息。例如它可能包括浏览器设置的语言,主体内容的长度等等。每个头部通过一个回车换行符(CRLF)来分隔的。
         请求主体内容:对于HTTP请求格式来说,头部和主体内容之间有一个回车换行符(CRLF)是相当重要的。CRLF告诉HTTP服务器主体内容是在什么地方开始的。在一些互联网编程书籍中,CRLF还被认为是HTTP请求的第四部分。
    3、 HTTP响应也包括三个部分: 
    · 方法—统一资源标识符(URI)—协议/版本
    · 响应的头部
    · 主体内容
    二、服务器与客户端通讯
    1、服务器与客户端通讯需要用到两个部分:Socket(客户端)和ServerSocket(服务器端)
    (1)ServerSocket(java.net.ServerSocket, 服务器端套接字),要创建一个服务器套接字,你需要使用ServerSocket类提供的四个构造方法中的一个。你需要指定IP地址和服务器套接字将要进行监听的端口号。通常,IP地址将会是127.0.0.1,也就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的IP地址被称为是绑定地址。服务器套接字的另一个重要的属性是backlog,这是服务器套接字开始拒绝传入的请求之前,传入的连接请求的最大队列长度,四种构造方法分别为:
              ServerSocket ss = new ServerSocket();//创建一个未绑定的ServerSocket
              ServerSocket ss = new ServerSocket(int port);//创建一个绑定到某端口的ServerSocket
              ServerSocket ss = new ServerSocket(int port, int log);//创建一个绑定到某端口的ServerSocket,并设置了最大队列长度。
              ServerSocket ss = new ServerSocket(int port, int log, InetAddress address);//创建一个绑定到某地址、某端口的ServerSocket,并设置了最大队列长度。
              对于第四个构造方法,绑定地址必须是InetAddress的一个实例,一种构造InetAddress对象的简单的方法是调用它的静态方法getByName,传入一个包含主机名称的字符串,就像下面的代码一样。
                   InetAddress.getByName("127.0.0.1");
          创建ServerSocket的常用方法:  
                   new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
          代码构造了一个监听本地机器8080端口的ServletSocket,它的队列长度为1. 
          服务器创建后就保持等待状态(TCP协议,可靠的传输协议,是同步协议,即没有响应返回就一直等待)
    (2)Socket(java.net.Socket类,客户端套接字):需要知道要访问的服务器端的IP/主机名和端口号,即可向服务器端发送请求。可以用Socket的众多构造方法中的一个来建立Socket         
           new Socket ("yahoo.com", 80);
         一旦你成功创建了一个Socket类的实例,你可以使用它来发送和接受字节流。要发送字节流,你首先必须调用Socket类的getOutputStream方法来获取一个java.io.OutputStream对象。要发送文本到一个远程应用,你经常要从返回的OutputStream对象中构造一个java.io.PrintWriter对象。要从连接的另一端接受字节流,你可以调用Socket类的getInputStream方法用来返回一个java.io.InputStream对象。 
    (3)服务器端通过accept()方法来接收客户端的连接请求,并与客户端建立连接,同时返回一个Socket
              Socket s =  ss. accept();
    (4)通过Socket可以获得一个输入流和一个输出流, 输入流用于读取客户端请求数据,输出流用于向客户端返回响应信息。
    比如:InputStream input =  s.getInputStream();
              OutputStream output = s.getOutputStream();
    三、简单的Web服务器通讯示例(建议拷贝到MyEclipse来看并执行)
    1、服务器类
    package com.socket.httpservertest;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    public class HttpServer {
         public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
         // shutdown command
         private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
         // the shutdown command received
         private boolean shutdown = false;
         public static void main(String[] args)
         {
               HttpServer server = new HttpServer();
               server.await();
               
         }
         public void await() {
         //   System.out.println(System.getProperty("user.dir"));
               ServerSocket serverSocket = null;
               int port = 8080;
               try {
                    serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
               } catch (IOException e) {
                    e.printStackTrace();
                    System.exit(1);
               }
               while (!shutdown) {
                    Socket socket = null;
                    InputStream input = null;
                    OutputStream output = null;
                    try {
                         //接收了客户端发来的请求,否则一致是等待状态
                         socket = serverSocket.accept();
                         input = socket.getInputStream();
                         output = socket.getOutputStream();
                         // create Request object and parse
                         Request request = new Request(input);
                         request.parse(); //从请求中读取内容
                         // create Response object
                         Response response = new Response(output);
                         response.setRequest(request);
                         response.sendStaticResource();
                         // Close the socket
                         socket.close();
                         //check if the previous URI is a shutdown command
                         shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
                    }catch (Exception e)
                    {
                         e.printStackTrace ();
                         continue;
                    }
               }
        }
    }
    2、请求类
    package com.socket.httpservertest;
    import java.io.IOException;
    import java.io.InputStream;
    public class Request {
         private InputStream input;
         private String uri;
         public Request(InputStream input)
         {
               this.input = input;
         }
         
         public void parse() {
               // Read a set of characters from the socket
               StringBuffer request = new StringBuffer(2048);
               int i;
               byte[] buffer = new byte[2048];
               try {
                    i = input.read(buffer); //将从输入流取2048长度的内容存到buffer字节数组中,如果内容不到2048,数组其他空间剩余空着
               } catch (IOException e) {
                    e.printStackTrace();
                    i = -1;
               }
               
               for (int j=0; j<i; j++)
               {
                    request.append((char) buffer[j]);
               }
               System.out.print(request.toString());
               uri = parseUri(request.toString());
         }
         
         private String parseUri(String requestString) {
               int index1, index2;
               index1 = requestString.indexOf(' ');
               /*
                * http请求行的结构:方法 统一资源标识符(URI) 协议/版本(它们之间以空格分隔)
                * 例如:POST //examples/default.jsp HTTP/1.1
                */
               if (index1 != -1) {// index1 == -1表示没找到
                         index2 = requestString.indexOf(' ', index1 + 1);//从index+1位置开始寻找‘ ’
                         if (index2 > index1)
                         return requestString.substring(index1 + 1, index2);
                    }
               return null;
         }
         
         public String getUri()
         {
               return uri;
         }
    }
    3、响应类
    package com.socket.httpservertest;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    public class Response {
         private static final int BUFFER_SIZE = 1024;
         Request request;
         OutputStream output;
         public Response(OutputStream output) {
               this.output = output;
         }
         public void setRequest(Request request) {
               this.request = request;
         }
         
         public void sendStaticResource() throws IOException {
               byte[] bytes = new byte[BUFFER_SIZE];
               FileInputStream fis = null;
               try {
                    File file = new File(HttpServer.WEB_ROOT, request.getUri());
                    if (file.exists()) {
                         fis = new FileInputStream(file);
                         int ch = fis.read(bytes, 0, BUFFER_SIZE);
                         while (ch!=-1) { //ch==-1表示读到末尾了
                               output.write(bytes, 0, ch); //写出到浏览器
                               ch = fis.read(bytes, 0, BUFFER_SIZE);//再读会接上一次读的位置往下读,如果读到末尾就会返回-1,终止输出
                         }
                    } else {
                         // file not found
                         String errorMessage = "HTTP/1.1 404 File Not Found
    " + "Content-Type: text/html
    " + "Content-Length: 23
    " + "
    " + "<h1>File Not Found</h1>";
                         output.write(errorMessage.getBytes());
                    }
               }catch (Exception e) {
                    // thrown if cannot instantiate a File object
                    System.out.println(e.toString() );
               } finally {
                    if (fis!=null)
                         fis.close();
               }
         }
    }
    4、示例代码功能描述:浏览器输入http请求,比如http://localhost:8080/MyHtml.html;服务器接收请求后用输入流读取请求内容获得文件位置,再用文件输入流读取文件中的内容;最后给浏览器客户端返回响应,把html文件中的内容显示在浏览器上,如图:
     
    5、示例代码跨职能流程图:
     
     
  • 相关阅读:
    Python类的继承(进阶5)
    面向对象编程基础(进阶4)
    Python模块(进阶3)
    Python函数式编程(进阶2)
    python多线程
    Ternary Search Tree Java实现
    Trie和Ternary Search Tree介绍
    索引时利用K-邻近算法过滤重复歌曲
    Sql排名和分组排名
    Lucene和jackson冲突
  • 原文地址:https://www.cnblogs.com/bjh1117/p/7131459.html
Copyright © 2020-2023  润新知