基本步骤:
下面是代码的基本交互步骤:
- 创建httpserver 对象
- 绑定端口和主机,建立套接字连接
accept()
方法等待请求,阻塞当前线程- 创建reuqest 请求对象
- 获取
inputstream
解析请求信息 - 获取请求到uri,设置到Request对象
- 创建 response 响应对象
- 设置响应对象的request,拿到uri, 同时使用Io,读取请求对应到文件
outputstream
解析文件流数据,使用write 返回到客户端- 无论成功还是失败,关闭流(重要)
补充内容:
这里重点关注关于套接字的一些知识
ServerSocket
服务器套接字的另一个重要的属性是 backlog,这是服务器套接字开始==拒绝==传入的请求之前,传入的连接请求的==最大队列长度==。
parseUri() 处理逻辑
GET /index.html HTTP/1.1
。parse 方法从传递给 Requst 对象的套接字的 InputStream 中读取整个字节流并在一个缓冲区中存储字节数组。然后它使用缓冲区字节数据的字节来填入一个 StringBuffer 对象,并且把代表 StringBuffer 的字符串传递给 parseUri 方法。
Http请求对于servlet的操作
当第一次调用 servlet 的时候,加载该 servlet 类并调用 servlet 的 init 方法(仅仅一次)。
- 对每次请求,构造一个 javax.servlet.ServletRequest 实例和一个 javax.servlet.ServletResponse 实例。
- 调用 servlet 的 service 方法,同时传递 ServletRequest 和 ServletResponse 对象。
- 当 servlet 类被关闭的时候,调用 servlet 的 destroy 方法并卸载 servlet 类。
本章的第一个servlet容器不是全功能的。因此,她不能运行什么除了非常简单的servlet, 而且也不调用 servlet 的 init 方法和 destroy 方法。相反它做了下面的事情:
- 等待 HTTP 请求。
- 构造一个 ServletRequest 对象和一个 ServletResponse 对象。
- 假如该请求需要一个静态资源的话,调用 StaticResourceProcessor 实例的 process 方
法,同时传递 ServletRequest 和 ServletResponse 对象。
- 假如该请求需要一个 servlet 的话,加载 servlet 类并调用 servlet 的 service 方法,
同时传递 ServletRequest 和 ServletResponse 对象
StringManager
特点:
- 使用单例模式
- 每个实例会读取包对应的一个属性文件
- StringManager 类被设计成一个 StringManager实例可以被包里边的所有类共享
- getManager() 方法被 同步修饰,并且使用hashtable对于manager进行管理(tomcat4)
核心方法解释
SocketInputStream:套接字读取流,主要用于处理Http请求中的各种参数,为了提高效率,使用懒加载特性读取
- 回收检查流数据
- 检查空行,如果出现-1抛出结尾异常
- 获取servlet方法名称
- 如果缓冲区已经满了,则进行扩展
- 我们在内部缓冲区的尽头
- 如果到了缓冲区结尾,将指针归位
- 这里有一个关键点:System.arraycopy 用来扩展缓冲区
- 阅读协议
模块解释
关于部分模块的相关解释。
解析头部
- 你可以通过使用类的无参数构造方法构造一个 HttpHeader 实例。
- 一旦你拥有一个HttpHeader实例,你可以把它传递给SocketInputStream的readHeader
方法。假如这里有头部需要读取,readHeader 方法将会相应的填充 HttpHeader 对象。 假如再也没有头部需要读取了,HttpHeader实例的nameEnd和valueEnd字段将会置零。
- 为了获取头部的名称和值,使用下面的方法:
- String name = new String(header.name, 0, header.nameEnd);
- String value = new String(header.value, 0, header.valueEnd);
启动器
- 启动应用程序
- 连接器
- 创建一个 HttpRequest 对象
- 创建一个 HttpResponse 对象
- 静态资源处理器和 servlet 处理器
- 运行应用程序
startup 模块只有一个类,Bootstrap,用来启动应用的。connector 模块的类可以分为五组:
- 连接器和它的支撑类(HttpConnector 和 HttpProcessor)。
- 指代 HTTP 请求的类(HttpRequest)和它的辅助类。
- 指代 HTTP 响应的类(HttpResponse)和它的辅助类。
- Facade 类(HttpRequestFacade 和 HttpResponseFacade)。
- Constant 类
问题以及解决
如何避免在servlet调用连接器的时候,不需要请求参数可以避免掉getParamMap,getAttribute等昂贵开销的操作?
Tomcat 的默认连接器(和本章应用程序的连接器)试图不解析参数直到 servlet 真正需要它的时候,通过这样来获得更高效率
小知识补充
- System 在打印的时候 print 方法不会刷新输出。
- 在一个 servlet 容器里边,一个类加载器可以找到 servlet 的地方被称为资源库(repository)。
- 通过外观模式将Request对象的细节隐藏,setvlet调用内部无法知道,但是在解析的时候依然可以相互通信,只需要使用faced将接口进行一层包裹, 即可保证getUri()方法安全性
总结
书中第一个章节内容比较简单,后续章节代码的难度会逐渐上升,同时使用了不少的设计模式也是需要多加阅读理解和消化的
写在最后
这篇笔记目的是让更多人了解这本书,这本书算是一本神书,毕竟开发的原作者自己写的东西毫无疑问是一手知识了。