当前位置: 首页 > news >正文

java trunked_Http协议Content-Length详解

前言

http协议是互联网中最重要的协议之一,虽然看上去很简单,但是实际中经常遇到问题,我们就已经遇到好几次了。有长连接相关的,有报文解析相关的。对http协议不能一知半解,必须透彻理解才行。本文通过一个线上实例,详细介绍http协议中的content-length字段。

问题

我们的手机App在做更新时会从服务器上下载的一些资源,一般都是一些小文件,更新的代码差不多是下面这样的:

static void update() throws IOException {

URL url = new URL("http://172.16.59.129:8000/update/test.so");

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

if(conn.getResponseCode() == 200) {

int totalLength = conn.getContentLength();

BufferedInputStream in = new BufferedInputStream(conn.getInputStream());

byte[] buffer = new byte[512];

int readLength = 0;

int length = 0;

while((length=in.read(buffer)) != -1) {

readLength += length;

//进度条

System.out.println(((float)readLength) /((float)(totalLength)));

}

}

}

比如上面的代码更新一个so文件,先通过content-length获取文件的总大小,然后读Stream,每读一段,就计算出当前读的总大小,除以content-length,用来显示进度条。

结果weblogic从10升级到12后,content-length一直返回-1,这样就不能显示进度条了,但是文件流还能正常读。把weblogic重启了,一开始还能返回content-length,一会又是-1了。

原因分析

Http协议的请求报文和回复报文都有header和body,body就是你要获取的资源,例如一个html页面,一个jpeg图片,而header是用来做某些约定的。例如客户端与服务端商定一些传输格式,客户端先获取头部,得知一些格式信息,然后才开始读取body。

客户端: Accept-Encoding:gzip (给我压缩一下,我用的是流量,先下载下来我再慢慢解压吧)

服务端1:Content-Encoding:null(没有Content-Encoding头。 我不给压缩,CPU没空,你爱要不要)

服务端2:Content-Encoding:gzip (给你节省流量,压缩一下)

客户端:Connection: keep-alive (大哥,咱好不容易建了个TCP连接,下次接着用)

服务端1: Connection: keep-alive (都不容易,接着用)

服务端2: Connection: close (谁跟你接着用,我们这个TCP是一次性的,下次再找我还得重新连)

http协议没有三次握手,一般客户端向服务端请求资源时,以服务端为准。还有一些header并没有协商的过程,而是服务端直接告诉客户端按什么来。例如上述的Content-Length,是服务端告诉客户端body的大小有多大。但是!服务端并不一定能准确的提前告诉你body有多大。服务端要先写header,再写body,如果要在header里把body大小写进去,就得提前知道body大小。如果这个body是动态生成的,服务端先生成完,再开始写header,这样需要很多额外的开销,所以header里不一定有content-length。

那客户端怎么知道body的大小呢?服务器有三种方式告诉你。

1.服务器已经知道资源大小,通过content-length这个header告诉你。

Content-Length:1076(body的大小是1076B,你读取1076B就可以完成任务了)

Transfer-Encoding: null

2.服务器没法提前知道资源的大小,或者不愿意花费资源提前计算资源大小,就会把http回复报文中加一个header叫Transfer-Encoding:chunked,就是分块传输的意思。每一块都使用固定的格式,前边是块的大小,后面是数据,然后最后一块大小是0。这样客户端解析的时候就需要注意去掉一些无用的字段。

Content-Length:null

Transfer-Encoding:chunked (接下来的body我要一块一块的传,每一块开始是这一块的大小,等我传到大小为0的块时,就没了)

3.服务器不知道资源的大小,同时也不支持chunked的传输模式,那么就既没有content-length头,也没有transfer-encoding头,这种情况下必须使用短连接,以连接结束来标示数据传输结束,传输结束就能知道大小了。这时候服务器返回的header里Connection一定是close。

Content-Length:null

Transfer-Encoding:null

Connection:close(我不知道大小,我也用不了chunked,啥时候我关了tcp连接,就说明传输结束了)

实验

我通过nginx在虚拟机里做实验,默认nginx是支持chunked模式的,可以关掉。

使用的代码如下,可能会调整参数。

static void update() throws IOException {

URL url = new URL("http://172.16.59.129:8000/update/test.so");

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

//conn.setRequestProperty("Accept-Encoding", "gzip");

//conn.setRequestProperty("Connection", "keep-alive");

conn.connect();

if(conn.getResponseCode() == 200) {

System.out.println(conn.getHeaderFields().keySet());

System.out.println(conn.getHeaderField("transfer-encoding"));

System.out.println(conn.getHeaderField("Content-Length"));

System.out.println(conn.getHeaderField("Content-Encoding"));

System.out.println(conn.getHeaderField("Connection"));

}

}

1.nginx在开启chunked_transfer_encoding的时候

(1) 在reqeust header里不使用gzip,也就是不加accept-encoding:gzip

test.so文件大小

结果

100B

能正常返回content-length,没有transfer-encoding头

69M

能正常返回content-length,没有transfer-encoding头

3072M

能正常返回content-length,没有transfer-encoding头

可以发现nginx不管资源多大,如果客户端不接受gzip的压缩格式,就不会使用chunked模式,而且跟是否使用短连接没关系。

(2)在request header里加入gzip,accepting-encoding:gzip

test.so文件大小

结果

100B

没有content-length,transfer-encoding=trunked

69M

没有content-length,transfer-encoding=trunked

3072M

没有content-length,transfer-encoding=trunked

可以看到nginx在开启chunked_transfer_encoding,并且客户端接受gzip的时候,会使用chunked模式,nginx开启gzip后不会计算资源的大小,直接用chunked模式。

2.nginx关闭chunked_transfer_encoding

(1) 在reqeust header里不使用gzip,也就是不加accept-encoding:gzip

test.so文件大小

结果

100B

能正常返回content-length,没有transfer-encoding头

69M

能正常返回content-length,没有transfer-encoding头

3072M

能正常返回content-length,没有transfer-encoding头

因为能很容易的知道文件大小,所以nginx还是能返回content-length。

(2)在request header里加入gzip,accepting-encoding:gzip

test.so文件大小

结果

100B

没有content-length和transfer-encoding头,不论客户端connection为keep-alive还是close,服务端返回的connection头都是close

69M

没有content-length和transfer-encoding头,不论客户端connection为keep-alive还是close,服务端返回的connection头都是close

3072M

没有content-length和transfer-encoding头,不论客户端connection为keep-alive还是close,服务端返回的connection头都是close

这就是上面说的第三种情况,不知道大小,也不支持trunked,那就必须使用短连接来标示结束。

问题解决方案

咨询了中间件组的同事,以前也遇到类似的问题,因为升级了Weblogic导致客户端解析XML出错,因为使用了chunked模式,中间有一些格式化的字符,而客户端解析的代码并没有考虑chunked模式的解析,导致解析出错。

因为我们客户端必须用content-length展示进度,因此不能用chunked模式,Weblogic可以把chunked模式关闭。用下面的方法:

#!java weblogic.WLST

connect('username’,'password', 't3://localhost:7001')

edit()

startEdit()

cd("Servers/AdminServer/WebServer/AdminServer")

cmo.setChunkedTransferDisabled(true)

save()

activate()

exit()

改了之后,确实不返回chunked了,但是也没有content-length,因为Weblogic就是不提前获取文件大小,而是强制加了connection:close,也就是前边说的第三种,通过连接结束标识数据结束。最后只能把这些资源放倒apache里了。

总结

一个好的http客户端,必须充分实现协议,不然就可能出问题,浏览器对于服务端可能产生的各种情况都很好的做了处理,但是自己实现http协议的解析时一定得注意考虑多种情况。

相关文章:

  • java xml 排除_java – Hyperjaxb:按XML属性排除XML元素
  • java多线程核心技术_《Java多线程编程核心技术(第2版)》 —1 Java多线程技能...
  • 看java源码怎么学_3个阅读跟踪Java源码的小技巧,很实用!
  • java的类方法_【Java】类方法和实例方法
  • java 废弃注解_Java 注解
  • java从菜鸟_Java从菜鸟到精通(5) | 学步园
  • python数据格式_python数据类型
  • 下载mysql然后怎么安装_mysql 下载与安装
  • java 方法前加同步_Java同步的三种实现方式详解
  • mysql 事务的作用_使用Mysql事务的优点
  • java和android架构公众号_Android仿微信公众号界面
  • 连连看的连线怎么写java_求助 这个连连看的方法地方该怎么写
  • php java字节流_php中stream(流)的用法
  • java自动雨刷系统_【图文】汽车自动雨刷的使用指南
  • php 多线程处理_PHP CURL 多线程操作代码实例
  • 分享一款快速APP功能测试工具
  • [case10]使用RSQL实现端到端的动态查询
  • 【EOS】Cleos基础
  • ERLANG 网工修炼笔记 ---- UDP
  • hadoop集群管理系统搭建规划说明
  • PermissionScope Swift4 兼容问题
  • SpringBoot几种定时任务的实现方式
  • Transformer-XL: Unleashing the Potential of Attention Models
  • tweak 支持第三方库
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • 闭包--闭包之tab栏切换(四)
  • 反思总结然后整装待发
  • ------- 计算机网络基础
  • 前端路由实现-history
  • 用jquery写贪吃蛇
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • ​520就是要宠粉,你的心头书我买单
  • ​马来语翻译中文去哪比较好?
  • ​什么是bug?bug的源头在哪里?
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #162 (Div. 2)
  • #每天一道面试题# 什么是MySQL的回表查询
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (一)80c52学习之旅-起始篇
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)一些感悟
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .Net Web项目创建比较不错的参考文章