问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

transfer-encoding和content-length的不同实现

发布网友 发布时间:2022-04-21 12:17

我来回答

2个回答

热心网友 时间:2022-04-21 13:47

前段时间在项目中看到如下的代码:

HttpServletResponse response = (HttpServletResponse)servletResponse;
   response.setHeader("Transfer-Encoding", "utf8");
   filterChain.doFilter(servletRequest, servletResponse);

原意是想对输出的内容进行编码,却用错了响应头,结果这个错误的响应头对后面的客户端程序带来了许多麻烦。这里有必要对这个这块的内容进行详细地了解。

传输数据编码:Transfer-Encoding 
数据编码,即表示数据在网络传输当中,使用怎么样的保证方式来保证数据是安全成功地传输处理。可以是分段传输,也可以是不分段,直接使用原数据进行传输。 
有效的值为:Trunked和Identity. 
传输内容编码:Content-Encoding 
内容编码,即整个数据信息是在数据器端经过怎样的编码处理,然后客户端会以怎么的编码来反向处理,以得到原始的内容。这里的内容编码主要是指压缩编码,即服务器端压缩,客户端解压缩。 
可以参考的值为:gzip,compress,deflate和identity。 
传输内容格式:Content-Type 
内容格式,即接收的数据最终是以何种的形式显示在浏览器中。可以是一个图片,还是一段文本,或者是一段html。内容格式额外支持可选参数,charset,即实际内容的字符集。通过字符集,客户端可以对数据进行解编码,以最终显示可以看得懂的文字(而不是一段byte[]或者是乱码)。

3种描述信息,可以由下图来表示(来源于《Http权威指南》):

从上文中,可以看出,实际上原filter中的内容可能是想表达以下的意思:

response.setContentType("text/html;charset=UTF8");
//或者
response.setContentType("application/json;charset=UTF8");

内容长度:Content-Length 
内容长度,即表示整个传输内容的有效长度信息。客户端可以通过此头信息来判断接收的数据是否已经完全接收。此编码和transfer-encoding相冲突,因为transfer-encoding会通过额外的处理方式来改变数据的组织方式,就会改变实际的数据长度,如果客户端仍按照原content-length来处理的话,则不会接收到完整的数据。

由于transfer-encoding和content-length之间存在冲突问题,因此在服务端和客户端就会有相应的实现来支持相应的数据处理。整个处理过程按照RFC 2616来处理。

处理规则:(http://tools.ietf.org/html/rfc2616#page-119所述)

If a Content-Length header field (section 14.13) is present, its
 decimal value in OCTETs represents both the entity-length and the
 transfer-length. The Content-Length header field MUST NOT be sent
 if these two lengths are different (i.e., if a Transfer-Encoding
 header field is present). If a message is received with both a
 Transfer-Encoding header field and a Content-Length header field,
 the latter MUST be ignored.

即通常使用content-length来表示内容长度。但如果存在transfer-encodig时,就不能再使用content-length了,而且也不应该再出现content-length头。既使服务器端同时有2个头信息,content-length也应该被忽略。

服务器端实现Tomcat

tomcat在实现transfer-encoding时默认采用trunked传输,但如果应用指定追加了content-length,则会使用content-length的值,就不再追加transfer-encoding了。相应的实现在类AbstractHttp11Processor方法prepareResponse中(tomcat7.0.52版本):

//先判断是否存在,contentLength头,即应用调用setContentLength方法,如果调用了。这里获取的值肯定不为-1
long contentLength = response.getContentLengthLong();
   boolean connectionClosePresent = false;
   if (contentLength != -1) {
     headers.setValue("Content-Length").setLong(contentLength);
//因为使用了content-length,所以在数据传输时,就不再进行额外处理了,直接将原数据输出至客户端即可。
     getOutputBuffer().addActiveFilter
       (outputFilters[Constants.IDENTITY_FILTER]);
     contentDelimitation = true;
   } else {
     // If the response code supports an entity body and we're on
     // HTTP 1.1 then we chunk unless we have a Connection: close header
     connectionClosePresent = isConnectionClose(headers);
//这里即默认使用trunked,但需要有相应的前提
//如果客户端显示设置Connetion:close时,即表示客户端只会请求一次,不会使用Keep-Alive,这样的话,不需要使用trunked传输,
//因为客户端知道何时数据已经传输完,使用read() == -1即可判断
     if (entityBody && http11 && !connectionClosePresent) {
//使用ChunkedOutputFilter来对传输的数据二次处理,即分段传输
       getOutputBuffer().addActiveFilter
         (outputFilters[Constants.CHUNKED_FILTER]);
       contentDelimitation = true;
       headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
     } else {
//最后使用原始数据传输方式
       getOutputBuffer().addActiveFilter
         (outputFilters[Constants.IDENTITY_FILTER]);
     }
   }

此段代码将在应用处理完逻辑或者调用response.outputStream.write时会调用。详细的处理逻辑,可参考上文中的注释。

需要注意的是,由于Http工作在TCP/IP之上,因此数据的完整性保证已经不需要由Http来处理了。所以依靠trunked来保证数据完整性已经没有太大意义。现在trunked的意义在于针对keep alive传输时,trunked可以通过特殊的处理来告诉客户端(通过发送头长度0来标识),该次的数据已经响应完毕。客户端可以处理并再次使用该连接进行下一次处理了。所以在上面的trunked处理中,tomcat如果认为没有使用trunked的必要时,就不会强制使用trunked了(如connection:close这种一次性请求模型)

客户端实现HttpClient

httpclient(版本4.3.3)的主要实现在类EntityDeserializer中,即如何去获取数据并反向解码。实现方法为doDeserialize,主要的实现如下所示:

final long len = this.lenStrategy.determineLength(message);
   if (len == ContentLengthStrategy.CHUNKED) {
     entity.setChunked(true);
     entity.setContentLength(-1);
     entity.setContent(new ChunkedInputStream(inbuffer));
   } else if (len == ContentLengthStrategy.IDENTITY) {
     entity.setChunked(false);
     entity.setContentLength(-1);
     entity.setContent(new IdentityInputStream(inbuffer));
   } else {
     entity.setChunked(false);
     entity.setContentLength(len);
     entity.setContent(new ContentLengthInputStream(inbuffer, len));
   }

即通过判断lengh值来确定是使用不同的数据解析。解析出来的流处理共有3种不同的处理方式,即transfer-encoding中指定的chunked和identity,以及由content-length指定的处理方式。 
对length的判断逻辑如下所示:

final Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
   // We use Transfer-Encoding if present and ignore Content-Length.
   // RFC2616, 4.4 item number 3
//首先根据RFC26216 4.4中介绍的,首先处理transfer-encoding
   if (transferEncodingHeader != null) {
     final HeaderElement[] encodings;
       encodings = transferEncodingHeader.getElements();
     // The chunked encoding must be the last one applied RFC2616, 14.41
     final int len = encodings.length;
//只判断是否和trunked和identity相等,在都不相等的情况下默认使用identity,以避免解析出错的情况
     if (HTTP.IDENTITY_CODING.equalsIgnoreCase(transferEncodingHeader.getValue())) {
       return IDENTITY;
     } else if ((len > 0) && (HTTP.CHUNK_CODING.equalsIgnoreCase(
         encodings[len - 1].getName()))) {
       return CHUNKED;
     } else {
       return IDENTITY;
     }
   }
//然后再使用content-length,这里同样判断,只有在确定好conten-length的情况下才使用,如果确定不好,仍然使用identity
   final Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
   if (contentLengthHeader != null) {
     long contentlen = -1;
     final Header[] headers = message.getHeaders(HTTP.CONTENT_LEN);
         contentlen = Long.parseLong(header.getValue());
     }
     if (contentlen >= 0) {
       return contentlen;
     } else {
       return IDENTITY;
     }
   }

在以上的处理当中,我们看到identity处理方式最多的。可以理解为,只要是不能解析的都使用identity,即原始处理方式。 
通过以上的实现,可以了解客户端在接收完数据之后的不同响应方式和处理逻辑,这也能解释在项目中的奇怪情况了。

问题出现及解决

在我们的项目中,由于上面的错误的filter的问题。我们在之前使用httpclient在接收数据时(使用httpPost),本来想接收一个json数据串,即总是返回类似以下的数据:

20
{abxxx.......}
0

这种情况只在处理我们请求的服务才会出现,请求其它之前的项目服务不会出现。多次发现这个特殊的值,好像表示数据长度。在不知道原因的情况下,我们通过在服务中加入以下代码之后,问题好像就解决了:

byte[] bytes = generatedBytes();//生成json bytes数组信息
response.setContentLength(bytes.length);//强制性设置contentLength值
response.getoutputStream.write(bytes);

但又有一个问题发生了,发现响应很慢。每次请求都要花费接近20s的时间,但监控服务代码,响应很快的。而且在服务返回之后,客户端需要继续等待一段时间才返回数据。经debug代码,最终发现httpclient在使用EntityUtils.toString中是这样写的:

final Reader reader = new InputStreamReader(instream, charset);
 final CharArrayBuffer buffer = new CharArrayBuffer(i);
 final char[] tmp = new char[1024];
 int l;
 while((l = reader.read(tmp)) != -1) {
     buffer.append(tmp, 0, l);
 }
 return buffer.toString();

这里的while循环在连接未关闭的情况下会一直等待。结合到keepAlive属性,这里肯定默认会使用keepAlive进行请求,而后服务器也肯定没有关闭连接。因此,我们又在使用httpClient时,强制加入以下头:

httpPost.addHeader("Connection","Close");

这样声明客户端只会请求一次,就断开连接。加入之后,好像问题再一次解决。

但这种解决方式怎么感觉也不是正规的解决方法,因为只是碰巧处理了这个问题。最终的解决思路,还是在认真了解了Http协议之后,并查看相应的服务代码之后,删除错误的输出头,直接就解决了此问题。至于增加content-length头,这个在删除header之后,可以直接删除了(因为增加了额外的代码)。

热心网友 时间:2022-04-21 15:05

根据你的描述;
transfer-encoding和content-length的不同实现,你可以参考下这篇文章,希望对你有所帮助!
http://www.tuicool.com/articles/FJ7rye
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
ef英语哪个好 EF英孚英语培训怎么样? 英孚英语好不好 EF英孚教育到底好不好 大佬们,麦芒7和荣耀10那个值得入手?2500以下的机子还有啥好推荐的么... 介绍几款2500元以前的手机 像素一定要高 其他的不做要求 近期想入手一部安卓手机,价格2200到2500左右…买HTC desire Z还是 三星... 笔记本忘记开机密码怎么办急死了 笔记本电脑屏幕开机锁忘记密码 怎么办?急死了 华硕笔记本电脑开机密码忘记了怎样找回?系统是Windows 7旗舰版... 治疗驼背有没有好办法? 磨砂油弄在电视柜上应该怎么办? 显示器上有油腻,该怎样清除? Excel在含有隐藏行的情况下,如何自动排序并忽略隐藏行,而且取消隐藏后,排序不会有任何变动 日本人有哪些 美国有微信 比利时也有微信,因为我都加过,不知道日本有没有微信? 日本能玩微信么 正的笔顺怎么写啊 正的笔顺笔画顺序图 正的正确笔画顺序 正笔画顺序怎么写 “正”这个字个正确笔顺是什么? 正的笔顺怎么写呀 正 字的笔顺是什么? 正的笔顺笔画顺序 制作电子报刊的任务,一下那个软件比较合适?word wps word写字板 还是word记事本 写字板 记事本? 不小心把OFFICE软件卸载了,之前所有的WORD文档都在写字板上,而且从网上下载word文档也不行,怎么办? 有个类似于记事本、写字板这样的软件,好像是w什么,是哪个软件?在哪下载 word写文档(写字板)软件下载 索尼摄像机AX45搭载的蔡司镜头拍摄效果如何? 一般驼背有什么好方法治疗 驼背有何好办法? 有什么比较好的方法可以改正驼背? 驼背怎么办,有什么好方法变好 驼背,最好的解决方法是什么? 如何更好的去除冰箱异味 IPS与防火墙有什么区别,都有什么用途,求得详细点的说明,qq:786678398,希望各位大侠审慎援助之手... 防火墙和IPS都有抗DDOS的功能,和专门的抗DDOS设备有什么区别呢? 防火墙和IPS都有抗DDOS的功能,和专门的抗DDOS设备有什么区别 抗DDOS攻击设备和防火墙,IPS,防毒墙功能一样吗? 新中国成立初期任是闭关锁国吗? 中国“闭关锁国”的具体表现是什么?对中国有何影响? L450QB是什么材质? 落后挨打只因闭关锁国?闭关锁国如何造成我国近代历史的屈辱? 中国对外政策由闭关锁国到被迫开放的原因? 为什么新中国成立后没能建立起全方位的对外开放格局 Φ660×14.3 L450直缝埋弧焊钢是什么意思 L450MB代表什么意思 readboy平板电脑维修点 忘了开机密码,咋办呀? 小叶增生怎么治?