套接字输入缓冲装置——InternalInputBuffer
互联网的世界很复杂,信息从一端传向另一端过程也相当复杂,中间可能通过若干个硬件,为了提高发送和接收效率,在发送端及接收端都将引入缓冲区,所以两端的套接字都拥有各自的缓冲区,当然这种缓冲区的引入也带来了不确定的延时,在发送端一般先将消息写入缓冲区,直到缓冲区填满才发送,而接收端则一次只读取最多不超过缓冲区大小的消息。
Tomcat在处理客户端的请求时需要读取客户端的请求数据,它同样需要一个缓冲区用于接收字节流,即套接字输入缓冲装置,它主要的责任是提供一种缓冲模式从socket中读取字节流,提供填充缓冲区的方法,即将字节读到缓冲区buf,提供解析http协议请求行的方法,提供解析http协议请求头的方法,按照解析的结果组装请求对象Request。
套接字输入缓冲装置的工作原理并不会复杂,如下图所示,InternalInputBuffer包含以下几个变量:字节数组buf、整型pos、整型lastValid、整型end。其中buf是用于存放缓冲的字节流,它的大小由程序设定,tomcat中默认是设置为8 * 1024,即8k字节;pos表示读取指针,读到哪个位置值即为多少;lastValid表示从操作系统底层读取数据填充到buf中最后的位置;end表示缓冲区buf中http协议请求报文头部结束的位置,同时也表示报文体的开始位置。
图中从上往下看,最开始缓冲区buf是空的,将socket操作系统底层的若干字节流读取到buf中,于是状态如②所示,读取到的字节流将buf从头往后进行填充,同时pos为0,lastValid为此次读取后最后的位置值,接着第二次读取操作系统底层若干字节流,每次读取多少是不确定,字节流应该接在②中lastValid指定的位置后面而非从头开始,此时pos及lastValid根据实际情况被赋予新值,假如再读取一次则最终状态为⑤,多出了一个end变量,它的含义是http请求报文的请求行及请求头结束的位置。
为了更好理解如何从底层读取字节流并进行解析,下面将给出简化的处理过程,首先需要一个方法提供读取字节流,如下,其中inputStream代表套接字的输入流,通过socket.getInputStream()获取,其中read方法用于读取字节流,它表示从底层读取最多(buf.length-lastValid)长度的字节流,且把这些字节流填入buf数组中,填充的起始位置为buf[pos]开始,nRead表示实际读取到的字节数。通过对上面这些变量的操作则可以准确操作缓冲装置,成功填充返回true。
publicclass InternalInputBuffer{
byte[] buf=newbyte[8*1024];
int pos=0;
int lastValid=0;
public booleanfill(){
int nRead = inputStream.read(buf,pos, buf.length - lastValid);
if (nRead >0) {
lastValid = pos + nRead;
}
return (nRead> 0);
}
}
有了填充的方法往下需要一个解析报文的操作过程,受篇幅影响此处只提供对请求行的方法及路径的解析为例子说明,其他的解析按照类似操作即可。http协议请求报文的格式如图,请求行一共有三个值需要解析出来:请求方法、请求url及协议版本,以空格间隔并以回车符换行符结尾。解析方法如下:
publicboolean parseRequestLine(){
int start = 0;
byte chr = 0;
boolean space = false;
while (!space) {
if (pos >= lastValid)
fill();
if (buf[pos] == (byte) ' ') {
space = true;
byte[] methodB = new byte[pos -start];
System.arraycopy(buf, start,methodB,0, pos - start);
String method = newString(methodB);
request.setMethod(method);
}
pos++;
}
while (space) {
if (pos >= lastValid)
fill();
if (buf[pos] == (byte) ' ') {
pos++;
} else {
space = false;
}
}
start = pos;
while (!space) {
if (pos >= lastValid)
fill();
if (buf[pos] == (byte) ' ') {
space = true;
byte[] uriB = newbyte[pos-start];
System.arraycopy(buf, start,uriB ,0, pos - start);
String uri = new String(uriB);
request.setUri(uri);
}
pos++;
}
return true;
}
第一个while循环用于解析方法名,每次操作前必须判断是否需要从底层读取字节流,当pos大于等于lastValid时即需要调用fill方法读取,当字节等于ASCII编码的空格时就截取start到pos之间的字节数组,它们便是方法名的字节组成,转成String对象后设置到request对象中;第二个while循环用于跳过方法名与uri之间所有的空格;第三个while循环用于解析uri,它的逻辑与前面方法名解析的逻辑差不多,解析到的uri最终也设置到request对象里中。
至此,整个缓冲装置的工作原理基本搞清楚了,一个完整的过程是从底层字节流的读取到对这些字节流的解析并组装成一个请求对象request方便程序后面使用,由于每次不能确切保证从底层读取到的字节流,于是通过对pos、lastValid变量进行控制以至于完成对字节流的准确读取接收。除此之外,输入缓冲装置还提供了解析请求头部的方法,处理逻辑是按照http协议的规定对头部解析,然后依次放入request对象中。需要额外说明的是,tomcat实际运行中并不会将请求行、请求头等参数解析后就转化为String类型设置到request,而是继续使用ASCII码存放这些值,因为对这些ASCII码转码会导致性能问题,它的思想是只有到需要使用的时候再进行转码,很多参数没使用到就不进行转码,以此提高处理性能。这方面详细内容在Request章节有涉及。
喜欢研究java的同学可以交个朋友,下面是本人的微信号:
套接字输入缓冲装置——InternalInputBuffer的更多相关文章
- 套接字输入流——InputStream
输入缓冲装置里面必须要包含读取字符的通道,否则就谈不上缓冲了,这个通道就是InputStream,它属于jdk中java.io包的类,有了它我们就可以从源头读取字符,它的来源可以有多种多样,这里主要探 ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- c 网络与套接字socket
我们已经知道如何使用I/O与文件通信,还知道了如何让同一计算机上的两个进程进行通信,这篇文章将创建具有服务器和客户端功能的程序 互联网中大部分的底层网络代码都是用C语言写的. 网络程序通常有两部分组成 ...
- Python黑帽编程2.8 套接字编程
Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...
- 【Python网络编程】利用Python进行TCP、UDP套接字编程
之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接 ...
- Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()
前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...
- 002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)
一.大致流程: 将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去.目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt. 二.数据结构: ICM ...
- 初探网络编程--TCP套接字编程演示
今天看了一下<计算机网络:自顶向下方法>,也就是计算机网络的教材的应用层一章,决定实现以下后面的Java C/S应用程序的例子,用来演示TCP和UDP套接字编程. 程序流程如下: 1.一台 ...
- C语言与套接字
我们已经知道如何使用I/O与文件通信,还知道了如何让同一计算机上的两个进程进行通信,这篇文章将创建具有服务器和客户端功能的程序 互联网中大部分的底层网络代码都是用C语言写的. 网络程序通常有两部分组成 ...
随机推荐
- Java内存分配、管理小结
转载自:http://java-mzd.iteye.com/blog/848635
- CSS3左右间歇晃动效果
今天在做一个活动页面时,产品想要在页面中添加一个吸引人注意的小图片左右晃动的效果,并且该效果是间歇执行的.我一想应该挺简单的吧,二话没说就答应了,谁知在真正实现的时候才发现还是有些许困难的.于是就在网 ...
- LeetCode 2
No1 Given a sorted array and a target value, return the index if the target is found. If not, return ...
- setTimeout、setInterval被遗忘的第三个参数
一.最近在看promise,惊奇的发现:原来 setTimeout不只有两个参数,我还能说什么呢?赶紧探探究竟. function multiply(input) { return new Promi ...
- vscode 常见插件及配置 备忘
配置 // 以下解决格式化js自动添加分号 "prettier.singleQuote": true, "prettier.semi": false, // 以 ...
- Day 1 Python简单程序
一.高级语言和低级语言 最初的计算机程序都是用0和1的序列表示的,程序员直接使用的是机器指令,无需翻译,从纸带打孔输入即可执行得到结果.后来为了方便记忆,就将用0.1序列表示的机器指令都用符号助记 ...
- 微信小程序--试水
应公司需求,接手小程序,在此之前我是一点也没有接触过,对此,拿过小程序文档和官方案例就一顿恶补,在此期间也看过一些小程序建立模型的视频,终于对小程序知晓一二,拿过项目开始研究.好了废话不多说,总结一下 ...
- Python之禅及其翻译
凡是用过 Python的人,基本上都知道在交互式解释器中输入 import this 就会显示 Tim Peters 的 The Zen of Python,但它那偈语般的语句有点令人费解,所以我想分 ...
- 如何搭建apache服务?
为了日后便于查询,本文所涉及到的所有命令集合如下: chkconfig iptables off #关闭防火墙命令 在Centos7中使用的是chkconfig firewalld off vi /e ...
- webpack4.x配置详解,多页面,多入口,多出口,新特性新坑!!
花了差不多一天多的时间,重新撸了一遍webpack4.x的常用配置. 基本上常用的配置都熟悉了一遍,总体上来讲,为了对parcel进行反击,webpack从4.x开始,正在朝着尽可能的简化配置文件的方 ...