作者 : 万境绝尘

转载请注明出处http://blog.csdn.net/shulianghan/article/details/25996817

工信部规定的网速測试标准 : 除普通网页測速採用单线程外,用户宽带接入速率測试应使用多线程(多TCP连接)HTTP下载进行測速,測试中使用的线程数量为N(N≥4)。

-- 建立连接 : 用户终端设备发起測试请求后,与測速平台建立 N 条 TCP 连接,并在每一条 TCP 连接上发送HTTP[GET]请求发起一次測试过程。
-- 请求文件 : 对每个 HTTP[GET]请求,宽带接入速率測试平台以 HTTP 200 OK 响应,并開始传送測速文件。
-- 下载文件 : 对每一条连接,宽带接入速率測试平台持续从内存直接发送 64kByte 大小的内容。
-- 平均速率 : 从收到第 1 个 HTTP[GET]请求開始计时,宽带接入速率測试平台及client软件每隔 1s 统计已经发送的文件大小,计算数据平均传送速率,并在网页上或client中实时更新。

-- 实时速率 : 宽带接入速率測试平台同一时候计算每 1s 间隔内的实时数据传送速率。
-- 測量时间 : 15s 后宽带接入速率測试平台停止发送数据,计算第 5s 到第 15s 之间共计 10s 的平均速率及峰值速率,峰值速率为步骤 5)中的每秒实时速率的最大值.

一. 网速測试核心代码

从GitHub上下载的源代码, 应该没有依照工信部的标准写的;

在 GitHub 上找到的网速測试的核心代码 :

-- GitHub 地址 : https://github.com/Mobiperf/Speedometer.git ;

  /** Runs the HTTP measurement task. Will acquire power lock to ensure wifi is not turned off */
@Override
public MeasurementResult call() throws MeasurementError { int statusCode = HttpTask.DEFAULT_STATUS_CODE;
long duration = 0;
long originalHeadersLen = 0;
long originalBodyLen;
String headers = null;
ByteBuffer body = ByteBuffer.allocate(HttpTask.MAX_BODY_SIZE_TO_UPLOAD);
boolean success = false;
String errorMsg = "";
InputStream inputStream = null; try {
// set the download URL, a URL that points to a file on the Internet
// this is the file to be downloaded
HttpDesc task = (HttpDesc) this.measurementDesc;
String urlStr = task.url; // TODO(Wenjie): Need to set timeout for the HTTP methods
httpClient = AndroidHttpClient.newInstance(Util.prepareUserAgent(this.parent));
HttpRequestBase request = null;
if (task.method.compareToIgnoreCase("head") == 0) {
request = new HttpHead(urlStr);
} else if (task.method.compareToIgnoreCase("get") == 0) {
request = new HttpGet(urlStr);
} else if (task.method.compareToIgnoreCase("post") == 0) {
request = new HttpPost(urlStr);
HttpPost postRequest = (HttpPost) request;
postRequest.setEntity(new StringEntity(task.body));
} else {
// Use GET by default
request = new HttpGet(urlStr);
} if (task.headers != null && task.headers.trim().length() > 0) {
for (String headerLine : task.headers.split("\r\n")) {
String tokens[] = headerLine.split(":");
if (tokens.length == 2) {
request.addHeader(tokens[0], tokens[1]);
} else {
throw new MeasurementError("Incorrect header line: " + headerLine);
}
}
} byte[] readBuffer = new byte[HttpTask.READ_BUFFER_SIZE];
int readLen;
int totalBodyLen = 0; long startTime = System.currentTimeMillis();
HttpResponse response = httpClient.execute(request); /* TODO(Wenjie): HttpClient does not automatically handle the following codes
* 301 Moved Permanently. HttpStatus.SC_MOVED_PERMANENTLY
* 302 Moved Temporarily. HttpStatus.SC_MOVED_TEMPORARILY
* 303 See Other. HttpStatus.SC_SEE_OTHER
* 307 Temporary Redirect. HttpStatus.SC_TEMPORARY_REDIRECT
*
* We may want to fetch instead from the redirected page.
*/
StatusLine statusLine = response.getStatusLine();
if (statusLine != null) {
statusCode = statusLine.getStatusCode();
success = (statusCode == 200);
} /* For HttpClient to work properly, we still want to consume the entire response even if
* the status code is not 200
*/
HttpEntity responseEntity = response.getEntity();
originalBodyLen = responseEntity.getContentLength();
long expectedResponseLen = HttpTask.MAX_HTTP_RESPONSE_SIZE;
// getContentLength() returns negative number if body length is unknown
if (originalBodyLen > 0) {
expectedResponseLen = originalBodyLen;
} if (responseEntity != null) {
inputStream = responseEntity.getContent();
while ((readLen = inputStream.read(readBuffer)) > 0
&& totalBodyLen <= HttpTask.MAX_HTTP_RESPONSE_SIZE) {
totalBodyLen += readLen;
// Fill in the body to report up to MAX_BODY_SIZE
if (body.remaining() > 0) {
int putLen = body.remaining() < readLen ? body.remaining() : readLen;
body.put(readBuffer, 0, putLen);
}
this.progress = (int) (100 * totalBodyLen / expectedResponseLen);
this.progress = Math.min(Config.MAX_PROGRESS_BAR_VALUE, progress);
broadcastProgressForUser(this.progress);
}
duration = System.currentTimeMillis() - startTime;
} Header[] responseHeaders = response.getAllHeaders();
if (responseHeaders != null) {
headers = "";
for (Header hdr : responseHeaders) {
/*
* TODO(Wenjie): There can be preceding and trailing white spaces in
* each header field. I cannot find internal methods that return the
* number of bytes in a header. The solution here assumes the encoding
* is one byte per character.
*/
originalHeadersLen += hdr.toString().length();
headers += hdr.toString() + "\r\n";
}
} PhoneUtils phoneUtils = PhoneUtils.getPhoneUtils(); MeasurementResult result = new MeasurementResult(phoneUtils.getDeviceInfo().deviceId,
phoneUtils.getDeviceProperty(), HttpTask.TYPE, System.currentTimeMillis() * 1000,
success, this.measurementDesc); result.addResult("code", statusCode); if (success) {
result.addResult("time_ms", duration);
result.addResult("headers_len", originalHeadersLen);
result.addResult("body_len", totalBodyLen);
result.addResult("headers", headers);
result.addResult("body", Base64.encodeToString(body.array(), Base64.DEFAULT));
} Log.i(SpeedometerApp.TAG, MeasurementJsonConvertor.toJsonString(result));
return result;
} catch (MalformedURLException e) {
errorMsg += e.getMessage() + "\n";
Log.e(SpeedometerApp.TAG, e.getMessage());
} catch (IOException e) {
errorMsg += e.getMessage() + "\n";
Log.e(SpeedometerApp.TAG, e.getMessage());
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
Log.e(SpeedometerApp.TAG, "Fails to close the input stream from the HTTP response");
}
}
if (httpClient != null) {
httpClient.close();
} }
throw new MeasurementError("Cannot get result from HTTP measurement because " +
errorMsg);
}

二. 分析源代码中用到的 API

1. HttpClient

(1) HttpClient 接口

接口介绍 : 这是一个 http client接口, 该接口中封装了一系列的对象, 这些对象能够运行 处理cookie 身份验证 连接管理等 http 请求; 线程安全的client都是基于 该接口 的实现和配置的;

接口方法 : 运行 各种 HttpRequest, 获取连接管理实例 , 获取client參数;

(2) AndroidHttpClient 类

类介绍 : 该类实现了 HttpClient 接口; 该类的本质是一个 DefaultHttpClient, 为Android 进行一些合理的配置 和 注冊规范, 创建该类实例的时候 使用 newInstance(String) 方法;

方法介绍 :

execute(HttpUriRequest) :

public HttpResponse execute (HttpUriRequest request)

-- 作用 : 使用默认的上下文对象运行 request请求;

-- 返回值 : 返回 request 的 response, 返回的是一个终于回应, 不会返回中间结果;

2. HttpUriRequest

(1) HttpUriRequest 接口

接口介绍 : 该接口实现了 HttpRequest 接口, 提供了方便的方法用于获取 request 属性, 比如 request的 uri 和 函数类型等;

方法介绍 :

-- 中断运行 : 中断 HttpRequest 的 execute()方法运行;

-- 获取uri : 获取request请求的 uri;

-- 获取方法 : 获取 request 请求的 方法, 比如 GET, POST, PUT 等;

-- 查询是否中断 : 查询是否运行了 abort()方法;

(2) HttpGet 类

类介绍 : Http 的 get 方法, 请求获取 uri 所标识的资源;

get方法 : 该方法会检索 请求地址 识别出来全部信息, 假设请求地址 引用了一个值, 这个值须要计算获得, 响应时返回的实体相应的是计算后的值;

方法特性 : getMethods 默认情况下会 遵循 http server的重定向请求, 这个行为能够通过调用 setFollowRedirects(false) 关闭;

(3) HttpPost 类

类介绍 : Http 的 Post 方法, 用于请求在 uri 指定的资源后附加的新数据;

Post方法功能 :

-- 凝视资源 : 给存在的资源加入凝视;

-- 发送信息 : 向 公告牌, 新闻组, 邮件列表 等发送信息;

-- 传输数据 : 如 表单提交到一个数据处理程序;

-- 数据库 : 通过一个附加操作 扩展数据库;

(4) HttpHead 类

类介绍 : HEAD 方法等价于 GET 方法, 除了在响应中不能返回方法体;

元信息 : HEAD 请求 与 GET 请求 的响应的消息头中的元信息是一样的;

方法作用 : 这种方法能够用来获取 请求中的元信息, 而不会获取 请求数据;

经常使用用途 : 检验超文本的可用性, 可达性, 和近期的改动;

3. HttpResponse

(1) HttpResponse 接口

接口介绍 : Http响应接口, 全部类型 HTTP 响应都应该实现这个接口;

方法介绍 :

-- 获取信息实体 : 假设有可能能够通过 setEntity()方法设置;

public abstract HttpEntity getEntity ()

-- 获取响应环境 : 依据环境确定 响应码相应的原因;

public abstract Locale getLocale ()

-- 获取状态行 : 获取响应的状态行

public abstract StatusLine getStatusLine ()

-- 设置响应实体

-- 设置响应环境 :

-- 设置状态行 :

-- 设置原因短语 : 使用原因短语更新状态行, 状态行仅仅能被更新, 不能显示的设置 或者 在构造方法中设置;

public abstract void setReasonPhrase (String reason)

-- 设置状态码 : 更新状态码, 状态码仅仅能更新, 不能显示的设置 或者在构造方法中设置;

public abstract void setStatusCode (int code)

(2) BasicHttpResponse 类

类介绍 : Http 响应的基本实现, 该实现能够被改动, 该实现确保状态行的存在;

方法介绍 : 该类 实现了 HttpResponse 接口, 实现了上述接口中的全部方法;

4. StatusLine

(1) StatusLine 接口

接口介绍 : 该接口代表从 HTTP server上返回的响应的状态行;

方法介绍 :

-- 获取协议版本 : getProtocalVersion();

-- 获取原因短语 : getReasonPhrase();

-- 获取状态码 : getStatusCode();

(2) BasicStatusLine

类介绍 : HTTP server响应的状态行;

方法介绍 : 实现了 StatusLine 的 3个 方法, 能够获取 协议版本, 原因短语, 状态码;

5. HttpEntity 接口

接口介绍 : HttpEntity 能够随着 HTTP 消息发送和接收, 在一些 请求 和 响应中能够找到 HttpEntity, 这是可选的;

HttpEntity 分类 :

-- 数据流 : 内容是从数据流中获取的, 或者是在内存中生成的, 通常, 这类 实体是从连接中获取的, 而且不可反复;

-- 独立的 : 内容从内存中获取, 或者从连接 或 其他 实体中获取的, 能够反复;

-- 包装 : 从其他实体中获取的;

三. 网速測试流程

a. 创建 AndroidHttpClient : 使用 AndroidHttpClient 的 newInstance(str)方法, 创建该实例, 创建实例的时候, 传入的字符串是 包名 + 版本, 自己组织;

AndroidHttpClient  httpClient = AndroidHttpClient.newInstance(packageName + " , " + version);

b. 创建 Http 请求 : 创建一个Get, Post 或者 Head 等类型的Http请求, 直接创建 HttpGet(url) 对象就可以;

      HttpRequestBase request = null;
if (task.method.compareToIgnoreCase("head") == 0) {
request = new HttpHead(urlStr);
} else if (task.method.compareToIgnoreCase("get") == 0) {
request = new HttpGet(urlStr);
} else if (task.method.compareToIgnoreCase("post") == 0) {
request = new HttpPost(urlStr);
HttpPost postRequest = (HttpPost) request;
postRequest.setEntity(new StringEntity(task.body));
} else {
// Use GET by default
request = new HttpGet(urlStr);
}

c. 创建缓冲区及相关数据 : 创建一个 byte[] 缓冲区, readLen 存储当前缓冲区读取的数据, totalBodyLen 存储全部的下载的数据个数;

      byte[] readBuffer = new byte[HttpTask.READ_BUFFER_SIZE];
int readLen;
int totalBodyLen = 0;

d. 运行 Http 请求 : 调用 HttpClient 的 execute() 方法;

HttpResponse response = httpClient.execute(request);

e. 获取响应的状态行 : 调用 响应 HttpResponse 的 getStatusLine() 方法获得;

StatusLine statusLine = response.getStatusLine();

f. 获取状态码 : 通过调用 状态行 statusLine 的 getStatusCode() 方法获得;

      if (statusLine != null) {
statusCode = statusLine.getStatusCode();
success = (statusCode == 200);
}

g. 获取响应实体 : 调用 响应 HttpResponse 的 getEntity() 方法获得;

HttpEntity responseEntity = response.getEntity();

h. 获取文件长度 : 调用 响应实体的 HttpEntity 的 getContentLength() 方法;

originalBodyLen = responseEntity.getContentLength();

i. 获取输入流 : 调用 响应实体 HttpEntity 的 getContent() 方法;

InputStream inputStream = responseEntity.getContent();

j. 从输入流中读取数据到缓冲区 : 调用 输入流的 read(buffer)方法, 该方法返回读取的字节个数;

readLen = inputStream.read(readBuffer)

注意 : 网速測试时要避免与硬盘的操作, 因此不能将数据村到磁盘上, 仅仅将数据存储到内存缓冲区中, 下一次缓冲区读取的时候, 直接将上一次的缓冲区内容覆盖擦除;

作者 : 万境绝尘

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/25996817

【Android 应用开发】Android 平台 HTTP网速測试 案例 API 分析的更多相关文章

  1. Android 平台 HTTP网速測试 案例 API 分析

    作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/25996817 工信部规定的网速測试标准 : 除普通网页測速 ...

  2. MAC中在eclipse luna上搭建移动平台自己主动化測试框架(UIAutomator/Appium/Robotium/MonkeyRunner)关键点记录

    这几天由于原来在用的hp laptop的电池坏掉了,机器一不小心就断电.所以仅仅能花时间在自己的mackbook pro上又一次搭建整套环境.大家都知道搭建好开发环境是个非常琐碎须要耐心的事情,特别是 ...

  3. 【Android 应用开发】Android 平台 HTTP网速测试 案例 API 分析

    作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/25996817 工信部规定的网速测试标准 : 除普通网页测速 ...

  4. Android 平台 HTTP网速测试 案例 API 分析

    作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/25996817 工信部规定的网速测试标准 : 除普通网页测速 ...

  5. android studio 开发android app 真机调试

    大家都知道开发android app 的时候可以有2种调试方式, 一种是Android Virtual Device(虚拟模拟器) ,另一种就是真机调试. 这里要说的是真机调试的一些安装步骤: 1. ...

  6. 码农人生——从未学过Android如何开发Android App 案例讲解-第002期案例

    标题有点晃眼,本次分享是002期博文的实践故事,不会有任何代码.也不会教别人android 如何开发,类似博文已经有大批大批,而且还会有陆陆续续的人写,我写的文章,主要是经验之谈,希望总结出的一些方法 ...

  7. 网络监测 断网 网速 ping 完整案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  8. Android +NDK+eclipse+opengl ES2.0 开启深度測试

    參考:https://www.opengl.org/discussion_boards/showthread.php/172736-OpenGL-ES-Depth-Buffer-Problem 环境: ...

  9. 执行Android项目时指定特定的AVD进行測试

    一个Androidproject空间能够创建一个或多个AVD来对指定的Android项目进行測试,假设仅仅创建了一个AVD则执行Android项目时自然启动该AVD,但是假设创建了多个AVD那么我们该 ...

随机推荐

  1. bzoj2260: 商店购物&&4349: 最小树形图

    最小树形图问题啊 最小树形图是撒哩,就是给你一个有向图,确定一个根,要你到达所有点,那棵最短路径树的总边权 做这个用的是朱(jv)刘(lao)算法. 首先假如有多个联通块就无解啦 对应每个点(除了根) ...

  2. bzoj4240: 有趣的家庭菜园(树状数组+贪心思想)

    4240: 有趣的家庭菜园 题目:传送门 题解: 好题!%%% 一开始不知道在想什么鬼,感觉满足二分性?感觉可以维护一个先单调增再单调减的序列? 然后开始一顿瞎搞...一WA 看一波路牌...树状数组 ...

  3. [BZOJ 3387] Fence Obstacle Course

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3387 [算法] f[i][0]表示从第i个栅栏的左端点走到原点的最少移动步数 f[i ...

  4. Java:笔记-1

    ylbtech-Java:笔记-1 1.返回顶部 1. /** * 简介请求 * @return */ @RequestMapping("/JJ") public String j ...

  5. Javascript技巧之不要用for in语句对数组进行遍历

    一,为什么不要用for in语句 jqModal这个jquery插件估计很多人都使用过,在jqModal源码内部,有一个函数为hs,其中有个嵌套循环如下, for(var i in {jqmShow: ...

  6. day63-webservice 07.07.如何修改cxf配置文件路径

    为什么第一次访问http://localhost:8080/cxf-web-server/service有点慢?证明第一次访问的时候CXFServlet被初始化了被加载了.一般是让CXFServlet ...

  7. thrift RPC 框架的自我搭建

    安装thrift rpc   安装的系统是Centos 7 未成功的方法 :(原因没找到,但是还是要记录下) 安装依赖库 yum install automake libtool flex bison ...

  8. 一篇个人感觉比较好的lua入门的文章

    原文转自www.cppprog.com,由三篇文章组成 Lua是一个嵌入式的脚本语言,它不仅可以单独使用还能与其它语言混合调用.Lua与其它脚本语言相比,其突出优势在于: 1.  可扩展性.Lua的扩 ...

  9. java selenium手动最大化chrome浏览器的方法

    package my_automation; import java.awt.Dimension; import org.openqa.selenium.Capabilities; import or ...

  10. 【MFC】模态、非模态对话框

    MFC 点击按钮,弹出另一个对话框 方法一:模态对话框 资源视图–Dialog–右键–添加资源–新建–对话框-,然后在已经生成的对话框中(解决资源视图中的dialog下的新生成的那个)右键–添加类.例 ...