Tomcat工作原理(一)一个简单的Web应用

tomcat 专栏收录该内容
4 篇文章 0 订阅

本章主要讲述一个java的web服务是如何工作的。
web也叫HTTP服务,因为他使用HTTP协议与客户端进行交互(通常使用的是浏览器),一个基于java的web服务主要涉及到2个重要的类:java.net.Socketjava.net.ServerSocket 并通过发送HTTP消息进行通信。

超文本传输协议(HTTP)

HTTP是一种协议,浏览器和web服务之间可以通过他在互联网上接收和发送数据。客户端请求一个文件而服务器端进行响应。HTTP使用的是TCP连接,默认端口80。现在HTTP协议基本上都是1.1版本。

HTTP请求

一个HTTP Request由以下三个部分组成:

  • Method—Uniform Resource Identifier (URI)—Protocol/Version
  • Request headers
  • Entity body

看一个实际的HTTP Request的例子:

//请求方法-URI-协议/版本号
POST /examples/default.jsp HTTP/1.1
//请求头
Accept: text/plain; text/html
Accept-Language: en-gb
Connection: Keep-Alive
Host: localhost
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
Content-Length: 33
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
//请求体
lastName=Franks&firstName=Michael

HTTP请求的请求方法有很多,GET,POST,HEAD,OPTIONS,PUT,DELETE和TRACE,GET和POST是应用中用的最多的请求方法。
URI-统一资源标识符,用于表示某一个互联网资源。他可以理解为是相对于服务器的根目录,所以以”/”开头
请求头包含客户端环境和请求体的有用信息,比如浏览器信息,请求体的长度设置等

HTTP响应

HTTP Response也包含有三个部分

  • Protocol—Status code—Description
  • Response headers
  • Entity body

看好一个响应的例子:

// 协议/版本-状态码-描述
HTTP/1.1 200 OK
// 响应头
Server: Microsoft-IIS/4.0
Date: Mon, 5 Jan 2004 13:13:33 GMT
Content-Type: text/html
Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT
Content-Length: 112
// 响应体
<html>
<head>
<title>HTTP Response Example</title>
</head>
<body>
Welcome to Brainy Software
</body>
</html>

Socket Class

Socket 是网络连接中的一个端点,通过socket可以使应用读写网络资源。通过此连接,两个不同的软件应用可以通过字节流进行通信。当然,前提是你需要知道对应应用的ip和端口号。在java中,socket是通过java.net.socket类呈现的。
Socket 类的构造方法有很多,例如:

//通过ip和端口
Socket(InetAddress address, int port)
//通过主机名和端口
Socket(String host, int port)
//创建实例
new Socket ("yahoo.com", 80);

其他的就不一一列举了
下面来看一段代码:

public class SocketClass {
    public static void main(String[] args) {
        try {
            //建立连接
            Socket client = new Socket("127.0.0.1",8080);
            //创建流
            OutputStream os  = client.getOutputStream();
            boolean autoFlush  = true;
            PrintWriter ps  =  new PrintWriter(client.getOutputStream(), autoFlush);
            BufferedReader br  = new BufferedReader(new InputStreamReader(client.getInputStream()));
            //向web服务器发送HTTP请求
            ps.println("GET /index.jsp HTTP/1.1");
            ps.println("Host: localhost:8080");
            ps.println("Connection: Close");
            ps.println();
            boolean loop = true;
            StringBuffer sb = new StringBuffer(8096);
            while (loop) {
            if ( br.ready() ) {
                int i=0;while (i!=-1) {
                    i = br.read();
                    sb.append((char) i);
                }
                loop = false;
            }
            Thread.currentThread().sleep(50);
            }
            // display the response to the out console
            System.out.println(sb.toString());
            client.close();

        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这是客户端代码,向服务器发送请求。找根目录下的index.jsp页面。并打印相应信息。

ServerSocket class

上述的Socket class代表的实际上是一个客户端的socket,无论何时,如果你想和服务器取得连接,只需要通过构造方法创建对象即可。
而ServerSocket与Socket不同之处是它必须任何时候都处于等待连接的状态。需要用到的类:java.net.ServerSocket
ServerSocketSocket不同,它的定位就是等待客户端连接,服务器端socket到一个连接请求,它就会创建一个Socket实例来和客户端的Socket 进行通信
使用构造创建ServerSocket:
常用的构造有

public ServerSocket(int port, int backLog, InetAddress bindingAddress);
这里被绑定的地址必须是 InetAddress 的对象,我们一般可以调用InetAddress.getByName("127.0.0.1"); 来获取他的对象。
如:
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));

应用代码

我们编写三个类来作为一个简单的服务器应用

  • HttpServer
  • Request
  • Response

The HttpServer Class:

public class HttpServer {

  /** WEB_ROOT 是放html和file文件的地方
   *  这个应用中,WEB_ROOT 就是项目目录下的webroot文件夹
   */
   //获取项目路径下的webroot目录
  public static final String WEB_ROOT =
    System.getProperty("user.dir") + File.separator  + "webroot";
  // 关闭命令
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
  // 接收关闭命令
  private boolean shutdown = false;
  public static void main(String[] args) {
    HttpServer server = new HttpServer();
    server.await();
  }
  public void await() {
    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);
    }
    // Loop waiting for a request
    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
      //等待接收
        socket = serverSocket.accept();
        input = socket.getInputStream();
        output = socket.getOutputStream();
        // 创建Request对象(Request实现代码在下面)
        Request request = new Request(input);
        request.parse();
        // 创建Response对象
        Response response = new Response(output);
        response.setRequest(request);
        response.sendStaticResource();
        // 关闭socket
        socket.close();
        //检测先前的URI是否是关闭命令
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        continue;
      }
    }
  }
}

The Request class:

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);
    }
    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());
  }
  //截取 "请求"内容的两个"空格"之间的uri
  private String parseUri(String requestString) {
    int index1, index2;
    index1 = requestString.indexOf(' ');
    if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }
  public String getUri() {
    return uri;
  }

}

The Response class:

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;
  }
  //读取request对象的URI中的内容
  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) {
          output.write(bytes, 0, ch);
          ch = fis.read(bytes, 0, BUFFER_SIZE);
        }
      }
      else {
        // file not found
        String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
          "Content-Type: text/html\r\n" +
          "Content-Length: 23\r\n" +
          "\r\n" +
          "<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();
    }
  }
}

在上述的web的应用中,新建了一个ServerSocket,并且处于接受连接状态,他可以接收来自浏览器的请求:http://127.0.0.1/index.html后, 会在浏览器中显示对应的页面(需要在项目路径的webroot下面放入想显示的index.html页面),当接受http://127.0.0.1/SHUTDOWN 后,会关闭连接。

  • 2
    点赞
  • 0
    评论
  • 2
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

最伤离别

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值