因为内容复杂繁多,预计分三次完成

一、回顾聊天室应用

拥有一个服务器端用来收到客户端的消息并实现业务逻辑、一个客户端发送消息
注意,这里并没有使用任何应用层面的协议,只简单的发送转发等,没有添加额外内容。
image.png
不过,显示上在发送消息过程中只是这么简单的转发发送消息是远远不够的,如果BUFFER尺寸不够大,消息残缺也不会报错,我们需要能确保消息发送完整等等特别的应用层协议。

二、网络应用

除了聊天室应用我们还需要知道一些其他应用广泛的应用——网络应用
同样,网络应用也是只是需要一个客户端和一个服务器端进行收发消息的操作。
image.png
其中请求及分为两大类:静态资源和动态资源。

静态资源

它拥有如下几个特点:

  • 不因请求的次数或顺序而变化
  • HTML、CSS、GIF、PNG
  • 服务器直接通过路径获取静态资源

    请求静态资源

    客户端请求资源,服务器在获取请求以后会根据相应路径获取资源然后响应给客户端。
    image.png

    动态资源

    如果只有静态资源网络会变得无趣,网络中需要一些比如随时间等变量变化变化的资源

    它有如下几个特点:

  • 随着请求方/发起时间/请求内容等因素而变化

  • 目前的商品库存量
  • 服务器通过容器获取动态资源

    请求动态资源

    客户端请求资源,服务器会请求容器,容器又会通过Servlet来获取动态资源,然后逐步返回最后响应客户端。
    image.png

    三、Tomcat结构

    Tomcat是一个开源的网络服务器,它支持开发人员编译自己的Servlet发送到服务器中,概要结构如图:
    image.png

    接下来解析一下基本结构。

    由图注意到在最里面才包裹着Servlet,在外面一层一层的包裹有什么用呢?+

    Server:

    拥有以下几个特点:

  • Tomcat服务器最顶层的组件

  • 负责运行Tomcat服务器
  • 负责加载服务器资源和环境变量

    Service:

    拥有如下几个特点:

  • 集合Connector和Engine的抽象组件(盒子)

  • 一个Server可以包含多个Service
  • 一个Service可以包含多个Connector和Engine

    Connector和Processor:

    它主要负责提供给服务器的客户一个可以和服务器创建连接的端点,它负责接收请求,负责把相应递交给服务器,它也可以根据请求做出基本的解析来判断下一步如何操作,但它不做具体操作,它会把请求传递给Processor,Processor再把资源传递给Engine。

    概括一下:

    Connector负责和外界链接建立和请求和响应发送。
    Processor负责从Connector接收到请求。

    总结:

  • Connector提供基于不同特定协议的实现

  • Connector接受解析请求,返回响应
  • 经Processor派遣请求至Engine进行处理

    Engine:

    这里和以后的每一层都被称为容器

    拥有特点如下:

  • 容器时Tomcat用来处理请求的组件

  • 容器内部的组件按照层级排列
  • Engine是容器的顶层组件

    Host:

    即虚拟主机

    拥有特点如下:

  • Host代表一个虚拟主机

  • 一个Engine可以支持对多个虚拟主机的请求
  • Engine通过解析请求来决定将请求发送给哪一个Host

    Context:

    最复杂的组件

    拥有特点如下:

  • Context代表一个Web Application

  • Tomcat最复杂的组件之一
  • 应用资源管理,应用类加载,Servlet管理,安全管理

    Wrapper:

    拥有特点如下:

  • Wrapper是容器最底层的组件

  • 包裹住Servlet实例
  • 负责管理Servlet实例的生命周期

    四、精简版的服务器设计

    省略掉Engine、Host、Context、Wrapper

    五、实现Request

    抽象出需要操作的请求 ```java package connector;

import java.io.IOException; import java.io.InputStream;

public class Request { private static final int BUFFER_SIZE = 1024;

  1. /**
  2. * 和Socket对应的InputStream,通过这里真正的读到quest
  3. */
  4. private InputStream input;
  5. /**
  6. * 具体的资源名字
  7. */
  8. private String uri;
  9. public Request(InputStream input) {
  10. this.input = input;
  11. }
  12. public String getRequestUri() {
  13. return uri;
  14. }
  15. /**
  16. * 解析请求
  17. */
  18. public void parse() {
  19. int length = 0;
  20. byte[] buffer = new byte[BUFFER_SIZE];
  21. try {
  22. length = input.read(buffer);
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. StringBuilder request = new StringBuilder();
  27. for (int j = 0; j < length; j++) {
  28. request.append((char) buffer[j]);
  29. }
  30. //进一步解析
  31. uri = parseUri(request.toString());
  32. }
  33. private String parseUri(String request) {
  34. int index1, index2;
  35. index1 = request.indexOf(" ");
  36. if (index1 != -1) {
  37. index2 = request.indexOf(" ", index1 + 1);
  38. if (index2 != -1) {
  39. return request.substring(index1 + 1, index2);
  40. }
  41. }
  42. return "";
  43. }

}

  1. <a name="7k8Hw"></a>
  2. # 六、测试Request
  3. ```java
  4. package test.connector;
  5. import connector.Request;
  6. import org.junit.jupiter.api.Assertions;
  7. import org.junit.jupiter.api.Test;
  8. import java.io.ByteArrayInputStream;
  9. import java.io.InputStream;
  10. public class RequestTest {
  11. private static final String VALID_REQUEST="GET /index.html HTTP/1.1";
  12. @Test
  13. public void givenValidRequestThenExtrackUri(){
  14. InputStream input=new ByteArrayInputStream(VALID_REQUEST.getBytes());
  15. Request request=new Request(input);
  16. request.parse();
  17. Assertions.assertEquals("/index.html",request.getRequestUri());
  18. }
  19. }

通过:
image.png