笔者昨晚上了一节关于Tomcat读取数据的底层运行细节的课,其实反反复复的都是对数据的多读或者少读做处理,但是就是这样的一节课也上了两个多小时。。。不过中途有一点点细节还是值得记录下的。


目录

  • 一、Tomcat是如何从TCP连接中获取数据的?
  • 二、Tomcat读取请求体的两种方式
  • 1、使用Content-Length来确定请求的结束位置
  • 2、使用Transfer-Encoding来确定请求结束的位置



一、Tomcat是如何从TCP连接中获取数据的?

主要是通过输入流,从socket的缓冲区拿到对应请求的输入流,然后将它转化为字节数组,再根据Http协议定下的格式来甄别出想要的数据(主要就是请求行、请求头)。

有个细节需要注意一下:
tomcat读数据的时候只会读取相关数据在字节数组中的下标开始位置和结束位置,并不会一步到位的将字节转化成对应的字符串,这样可以减少内存的消耗品

二、Tomcat读取请求体的两种方式

读取请求体的方式其实就是确认一个请求结束位置的方法,这种方法有两种:

1、使用Content-Length来确定请求的结束位置

一般来说这个方式是用在请求头部的,tomcat在读取请求体的时候会先通过设置在请求头部的这个字段来确定请求体的长度,然后在读取请求体的时候读取出对应长度的请求体数据,然后将之后多出来的数据当成下一个请求的请求行(Content-Length在请求方法为GET的时候不能使用,原因是GET方法没有请求体)。

吐槽一句,昨晚那个讲师就是在请求体这里围绕多读数据和少读数据反复展开解读源码,讲了一晚上。还有就是笔者发现个攻击Tomcat的方式,如果将Content-Length长度足够长,而当前请求的请求体加上下一个请求的所有http数据才能达到这个长度,那么下一个请求是不是会被完全忽略掉,因为下一个请求的数据已经完全被上一个请求体读取掉了。

2、使用Transfer-Encoding来确定请求结束的位置

对于某些请求体长度不好判断的请求来说,使用Content-Length可能会增加内存的开销,因为需要给出一个足够大的内存去统计大小。而此时Transfer-Encoding就可以派上用场了,它使用的分块上传的方式,将一整块数据分成许多分然后分块上传,然后以 0/r/n /r/n结尾。具体的请求体格式如下:

5\r\n
人间烟火气\r\n

5\r\n
最抚凡人心\r\n

0\r\n
\r\n

注意:上述两种方式不仅仅是请求头用来确定结束位置的标志,响应头也可以用这两种方式来确定响应的结束位置,不过Content-Length一般用于请求头,而Transfer-Encoding一般用于响应头(因为无法知道服务端能否解析这个请求头,但是浏览器能解析这个响应头)

再拓展一下:
tomcat只会处理表单的content-type为application/x-www.-form-urlencoded和multipart/form-data的参数。springmvc之所以能接受到content-type=application/json类型的数据,是因为它本身实现了对json数据的读取工作,也就是从输入流中将数据取出来然后封装成服务端想要的json数据格式。