-
C层实现多线程测网速
2013-09-06 09:02:40可能这个程序有很多问题,所以请各位大婶批评指正,勿喷! Server端: #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 1024 ...可能这个程序有很多问题,所以请各位大婶批评指正,勿喷!
Server端:
#include<pthread.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include<time.h>#define BUFSIZE 1024
pthread_t thread[4];
double sock(int port){
int server_sockfd;
int client_sockfd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size;
char buf[BUFSIZE];
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=INADDR_ANY;
server_addr.sin_port=htons(port);
if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0){
perror("socket");
return 1;
}
if(bind(server_sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr))<0){
perror("bind");
return 1;
}
listen(server_sockfd,5);
sin_size=sizeof(struct sockaddr_in);
if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_addr,&sin_size))<0){
perror("accept");
return 1;
}
printf("accept client %s port%d\n",inet_ntoa(client_addr.sin_addr),port);
int buflen=128*1024;
setsockopt(client_sockfd,SOL_SOCKET,SO_RCVBUF,(const char *)&buflen,sizeof(int));
unsigned long count=0;
unsigned int length;
clock_t start,finish;
start=clock();
while((length=recv(client_sockfd,buf,BUFSIZE,0))>0){
if(length<0){
printf("receive failed!");
break;
}
count+=length;
}
finish=clock();
printf("The total count of byte is %ld bytes\n",count);
double time=(double)(finish-start)/CLOCKS_PER_SEC;
double v=(double)(count)/1024/time;
printf("v = %lf KB/s \n",v);
return v;
}
double v1,v2,v3,v4;
void *thread1()
{
v1=sock(8000);
pthread_exit(NULL);
}void *thread2()
{
v2=sock(8001);
pthread_exit(NULL);
}
void *thread3()
{
v3=sock(8002);
pthread_exit(NULL);
}
void *thread4()
{
v4=sock(8003);
pthread_exit(NULL);
}
void thread_create(void)
{
int temp;
memset(&thread,0,sizeof(thread));
if((temp=pthread_create(&thread[0],NULL,thread1,NULL))!=0)
printf("creating thread1 failed!\n");
else
printf("creating thread1 success!\n");
if((temp=pthread_create(&thread[1],NULL,thread2,NULL))!=0)
printf("creating thread2 failed!\n");
else
printf("creating thread2 success!\n");
if((temp=pthread_create(&thread[2],NULL,thread3,NULL))!=0)
printf("creating thread3 failed!\n");
else
printf("creating thread3 success!\n");
if((temp=pthread_create(&thread[3],NULL,thread4,NULL))!=0)
printf("creating thread4 failed!\n");
else
printf("creating thread4 success!\n");
}
void thread_wait(void)
{
if(thread[0]!=0)
{
pthread_join(thread[0],NULL);
printf("thread1 is finished!\n");
}
if(thread[1]!=0)
{
pthread_join(thread[1],NULL);
printf("thread2 is finished!\n");
}
if(thread[3]!=0)
{
pthread_join(thread[3],NULL);
printf("thread3 is finished!\n");
}
if(thread[4]!=0)
{
pthread_join(thread[4],NULL);
printf("thread4 is finished!\n");
}
}int main()
{
printf("I'm main function.I'm creating thread!\n");
thread_create();
printf("I'm main function.I'm waiting for thread to finish task!\n");
thread_wait();
printf("The total of vate is %lf KB/s\n",v1+v2+v3+v4);
return 0;
}Client端:
#include<pthread.h>
#include<stdio.h>
#include<sys/time.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<time.h>#define filename "/sdcard/ssss.mp3"
#define BUFSIZE 1024
#define MAX 10
#define TIMES 2500pthread_t thread[4];
int port1=0,port2=0,port3=0,port4=0;
char* addr;double sock(char* addr,char* port){
int i=TIMES;
int client_sockfd;
struct sockaddr_in server_addr;
char buf[BUFSIZE];
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=inet_addr(addr);
server_addr.sin_port=htons(atoi(port));
if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
perror("socket");
return 1;
}
printf("create client socket\n");
int buflen=128*1024;
setsockopt(client_sockfd,SOL_SOCKET,SO_SNDBUF,(const char *)&buflen,sizeof(int));
if(connect(client_sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr))<0)
{
perror("connect");
return 1;
}
printf("connected to server\n");
FILE* from_fd;
if((from_fd=fopen(filename,"r"))==-1)
{
printf("open or create file failed!");
exit(0);
}
int len=fread(buf,sizeof(char),BUFSIZE,from_fd);
clock_t start,finish;
start=clock();
while(i--){
if(send(client_sockfd,buf,len,0)<0){
printf("length<0\n");
break;
}
}
finish=clock();
double time=(double)(finish-start)/CLOCKS_PER_SEC;
double v=TIMES/time;
printf("v = %lf KB/s \n",v);
return v;
}
double v1,v2,v3,v4;
void *thread1()
{
v1=sock(addr,port1);
pthread_exit(NULL);
}void *thread2()
{
v2=sock(addr,port2);
pthread_exit(NULL);
}
void *thread3()
{
v3=sock(addr,port3);
pthread_exit(NULL);
}
void *thread4()
{
v4=sock(addr,port4);
pthread_exit(NULL);
}
void thread_create(void)
{
int temp;
memset(&thread,0,sizeof(thread));
if((temp=pthread_create(&thread[0],NULL,thread1,NULL))!=0)
printf("creating thread1 failed!\n");
else
printf("creating thread1 success!\n");
if((temp=pthread_create(&thread[1],NULL,thread2,NULL))!=0)
printf("creating thread2 failed!\n");
else
printf("creating thread2 success!\n");
if((temp=pthread_create(&thread[2],NULL,thread3,NULL))!=0)
printf("creating thread3 failed!\n");
else
printf("creating thread3 success!\n");
if((temp=pthread_create(&thread[3],NULL,thread4,NULL))!=0)
printf("creating thread4 failed!\n");
else
printf("creating thread4 success!\n");
}void thread_wait(void)
{
if(thread[0]!=0)
{
pthread_join(thread[0],NULL);
printf("thread1 is finished!\n");
}
if(thread[1]!=0)
{
pthread_join(thread[1],NULL);
printf("thread2 is finished!\n");
}
if(thread[2]!=0)
{
pthread_join(thread[2],NULL);
printf("thread3 is finished!\n");
}
if(thread[3]!=0)
{
pthread_join(thread[3],NULL);
printf("thread4 is finished!\n");
}
}int main(int argc,char *argv[])
{
addr=argv[1];
port1=argv[2];
port2=argv[3];
port3=argv[4];
port4=argv[5];
printf("I'm main function.I'm creating thread!\n");
thread_create();
printf("I'm main function.I'm waiting for thread to finish task!\n");
thread_wait();
printf("The total of vate is %lf KB/s\n",v1+v2+v3+v4);
exit(0);
}
另外有个小问题,sock函数返回double类型的时候,是不是会影响运行速度?
-
静之枫多线程网速测试工具.
2012-12-08 17:53:41可以测网速的工具。 -
【Android 应用开发】Android 平台 HTTP网速测试 案例 API 分析
2014-05-28 01:15:13工信部规定的网速测试标准 : 除普通网页测速采用单线程外,用户宽带接入速率测试应使用多线程(多TCP连接)HTTP下载进行测速,测试中使用的线程数量为N(N≥4)。 -- 建立连接 : 用户终端设备发起测试请求后,与测速平台...作者 : 万境绝尘
转载请注明出处 : 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]请求开始计时,宽带接入速率测试平台及客户端软件每隔 1s 统计已经发送的文件大小,计算数据平均传送速率,并在网页上或客户端中实时更新。
-- 实时速率 : 宽带接入速率测试平台同时计算每 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 客户端接口, 该接口中封装了一系列的对象, 这些对象可以执行 处理cookie 身份验证 连接管理等 http 请求; 线程安全的客户端都是基于 该接口 的实现和配置的;
接口方法 : 执行 各种 HttpRequest, 获取连接管理实例 , 获取客户端参数;
(2) AndroidHttpClient 类
类介绍 : 该类实现了 HttpClient 接口; 该类的本质是一个 DefaultHttpClient, 为Android 进行一些合理的配置 和 注册规范, 创建该类实例的时候 使用 newInstance(String) 方法;
方法介绍 :
execute(HttpUriRequest) :
-- 作用 : 使用默认的上下文对象执行 request请求;public HttpResponse execute (HttpUriRequest 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 服务器的重定向请求, 这个行为可以通过调用 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 服务器上返回的响应的状态行;
方法介绍 :
-- 获取协议版本号 : getProtocalVersion();
-- 获取原因短语 : getReasonPhrase();
-- 获取状态码 : getStatusCode();
(2) BasicStatusLine
类介绍 : HTTP 服务器响应的状态行;
方法介绍 : 实现了 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
-
[Python]_[初级]_[多线程下载单个文件]
2020-07-18 16:22:36有没有模块类似下载工具那样能多线程下载同一个文件? 如果没有多线程下载单个文件的模块,那我们应该如何编码实现功能? 说明 Python作为日常的部署语言,编写自动化脚本目前看来还是比较方便的,因为它的库...场景
-
使用
Python
做自动化测试时,有时候需要从网络下载软件安装包并安装。但是使用urllib
库时,默认都是单线程下载文件,如果文件比较小还好说,如果文件有20M
时,普通的网速就要等待很长的时间。有没有模块类似下载工具那样能多线程下载同一个文件? -
如果没有多线程下载单个文件的模块,那我们应该如何编码实现功能?
说明
-
Python
作为日常的部署语言,编写自动化脚本目前看来还是比较方便的,因为它的库很多,动态语言特性灵活,自动内存管理,编码到执行都无需编译等待等。 -
说到这个多线程下载单个文件,在
Python
的使用手册里,真没发现有相关的模块做这个功能。搜索了下也没简单能用的模块。 -
实现多线程下载同一个静态文件(注意是静态文件,而流式文件是获取不到大小的),原理就是每个线程下载文件的不同部分(一个文件可以看成不同大小的块组成),这样每个线程执行完之后,文件就全部下载完了。多线程下载速度也不一定是快的,要看下载网站的带宽出口,如果它的带宽出口比较小,那么多线程都会比单线程快。如果像腾讯那种大厂,它的网站带宽很大,还有
DDOS
检测防护,比你自己的网络带宽还大,所以基本上只用一个线程就很快了。 -
多线程下载文件的不同部分,首先需要发送
HEAD
请求获取http
头里的Content-Length
的文件大小,之后才能根据文件大小和线程个数分成多个块,每个块有起始位置和结束位置,而每个线程只下载自己的文件块就行了。 -
这里说明下,访问
https
和支持TLS
协议,需要安装额外的模块,请查看关于如何使用urllib3库和访问https的问题,总的说需要先通过pip
安装pyOpenSSL, cryptography, idna, certifi
模块。
pip install pyOpenSSL pip install cryptography pip install idna pip install certifi
- 之后如果想支持命令行参数管理,可以安装
click
模块。
pip install click
- 最后就是需要设定一个下载缓存大小,我这里设置为
100k
。可以根据自己的网速设定,太大的话,http
请求可能就会超过远程网站的返回大小导致速度很慢。之后还需要通过发送GET
请求,附带请求头内容属性Range
来获取文件指定范围的数据。当然如果某个线程负责下载的文件块过大,我们还需要分割为100k
的子块,循环请求多次直到完成下载负责的文件块。
headers = {"Range":"bytes=%d-%d"%(start,end)} res = self.http.request('GET',self.url,headers=headers,preload_content=False)
- 我这里的测试
Python
版本是3.7
.
代码
MultiThreadDownloadFile.py
import threading import time import urllib3 import urllib3.contrib.pyopenssl import certifi import click import random lock = threading.Lock() count = 0 def requestFileSize(http,url): r = http.request('HEAD',url) return r.headers["Content-Length"] def test_urllib3(http,url): header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36" } response = http.request('GET', url, None, header) data = response.data.decode('utf-8') # 注意, 返回的是字节数据.需要转码. print (data) # 打印网页的内容 class MulThreadDownload(threading.Thread): def __init__(self,http,url,startpos,endpos,fo): super(MulThreadDownload,self).__init__() self.url = url self.startpos = startpos self.endpos = endpos self.fo = fo self.http = http def downloadBlock(self,start,end): headers = {"Range":"bytes=%d-%d"%(start,end)} res = self.http.request('GET',self.url,headers=headers,preload_content=False) lock.acquire() self.fo.seek(start) global count count = (count+ len(res.data)) print("download total %d" % count) self.fo.write(res.data) self.fo.flush() lock.release() def download(self): print("start thread:%s at %s" % (self.getName(), time.process_time())) bufSize = 102400 pos = self.startpos+bufSize while pos < self.endpos: time.sleep(random.random()) # 延迟 0-1s,避免被服务器识别恶意访问 self.downloadBlock(self.startpos,pos) self.startpos = pos+1 pos = self.startpos + bufSize self.downloadBlock(self.startpos,self.endpos) print("stop thread:%s at %s" % (self.getName(), time.process_time())) def run(self): self.download() def createFile(filename,size): with open(filename,'wb') as f: f.seek(size-1) f.write(b'\x00') @click.command(help="""多线程下载单个静态文件,注意,目前不支持数据流文件.如果下载不了,请减少线程个数. \n MultiThreadDownloadFile.py pathUrl pathOutput""") @click.option('--threads_num',default=2, help="线程个数") @click.option('--url_proxy',default="", help="HTTP代理") @click.argument('path_url',type=click.Path()) @click.argument('path_output',type=click.Path()) @click.pass_context def runDownload(ctx,threads_num,url_proxy,path_url,path_output): print(" threadNum: %d\n urlProxy: %s\n pathUrl: %s\n PathOutput %s\n" % (threads_num,url_proxy,path_url,path_output)) http = None if len(url_proxy) == 0: http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) else: http = urllib3.ProxyManager(url_proxy,cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) print(path_url) print(http) fileSize = int(requestFileSize(http,path_url)) print(fileSize) step = fileSize // threads_num mtd_list = [] createFile(path_output,fileSize) startTime = time.time() # rb+ ,二进制打开,可任意位置读写 with open(path_output,'rb+') as f: loopCount = 1 start = 0 while loopCount < threads_num: end = loopCount*step -1 t = MulThreadDownload(http,path_url,start,end,f) t.start() mtd_list.append(t) start = end+1 loopCount = loopCount+1 t = MulThreadDownload(http,path_url,start,fileSize-1,f) t.start() mtd_list.append(t) for i in mtd_list: i.join() endTime = time.time() print("Download Time: %fs" % (endTime - startTime)) if __name__ == "__main__": urllib3.contrib.pyopenssl.inject_into_urllib3() random.seed() runDownload(obj = {})
下载
如何执行
python MultiThreadDownloadFile.py --help python MultiThreadDownloadFile.py http://dldir1.qq.com/invc/tt/QTB/Wechat_QQBrowser_Setup.exe setup.exe MultiThreadDownloadFile.exe --help MultiThreadDownloadFile.exe http://dldir1.qq.com/invc/tt/QTB/Wechat_QQBrowser_Setup.exe setup.exe
下载EXE独立文件
- 我使用
pyinstaller
打包了为单个独立的EXE
文件, 如果没有Python
环境的,可以从
https://gitee.com/tobey-robot/AutomaticPython/releases下载MultiThreadDownloadFile.zip
参考
-
-
Android 平台 HTTP网速测试 案例 API 分析
2015-01-14 14:25:16工信部规定的网速测试标准 : 除普通网页测速采用单线程外,用户宽带接入速率测试应使用多线程(多TCP连接)HTTP下载进行测速,测试中使用的线程数量为N(N≥4)。 -- 建立连接 : 用户终端设备发起测试请求作者 : 万境绝尘
转载请注明出处 : 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]请求开始计时,宽带接入速率测试平台及客户端软件每隔 1s 统计已经发送的文件大小,计算数据平均传送速率,并在网页上或客户端中实时更新。
-- 实时速率 : 宽带接入速率测试平台同时计算每 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 客户端接口, 该接口中封装了一系列的对象, 这些对象可以执行 处理cookie 身份验证 连接管理等 http 请求; 线程安全的客户端都是基于 该接口 的实现和配置的;
接口方法 : 执行 各种 HttpRequest, 获取连接管理实例 , 获取客户端参数;
(2) AndroidHttpClient 类
类介绍 : 该类实现了 HttpClient 接口; 该类的本质是一个 DefaultHttpClient, 为Android 进行一些合理的配置 和 注册规范, 创建该类实例的时候 使用 newInstance(String) 方法;
方法介绍 :
execute(HttpUriRequest) :
- public HttpResponse execute (HttpUriRequest 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 服务器的重定向请求, 这个行为可以通过调用 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 服务器上返回的响应的状态行;
方法介绍 :
-- 获取协议版本号 : getProtocalVersion();
-- 获取原因短语 : getReasonPhrase();
-- 获取状态码 : getStatusCode();
(2) BasicStatusLine
类介绍 : HTTP 服务器响应的状态行;
方法介绍 : 实现了 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
-
网络测试工具_【网速】一款流行的网络速度测试工具
2020-12-13 08:48:48一款流行的网络速度测试工具,线路遍布全球,包括非常多的中国线路,最近更新了多线程测速,以及在 Android 版本上添加了运营商信号地图覆盖功能,能够直接在地图上看到网络信号覆盖情况。今日关键词测网速如果你想... -
java 使用RandomAssessFile类多线程切片下载文件之服务端如何实现
2018-09-16 12:32:40上一篇我写了如何使用 java 结合网络连接+多线程+RandomAssessFile类实现多线程切片下载并显示网速https://blog.csdn.net/yali_aini/article/details/81942036 因为之前写的都是客户端,不需要去管服务端,直接把... -
编写多线程并发的测试类
2017-09-24 11:27:21在实际运用的过程中,我们经常遇到一些...2.一个下订单接口,用户要是操作速度或者网速的原因导致两个请求同时到达服务器,难道就插入两条订单么?同样,说不定还得邮件反省。 事由: 1.集群分布式部署导致synchroni -
线程的基本操作(五)---中断
2019-10-27 13:38:11在线程执行过程中,有很多场景需要终止线程的运行。这时就需要一个方法来结束线程并返回结果。 中断的方法 Thread类中提供了3个有关中断的方法: interrupt():设置中断标志位为true interrupted();检测中断标志,... -
iocp udp 分布式队列无同步多核测试
2010-06-30 23:45:01此是自己优化多核多线程测试用的一个基准程序,是一个udp 的iocp实现,不具多少实用性,但具有研究性.采用分布式队列思想,尽量减少因同步而导致的线程切换带来的开销.在程序中采用tls,使每个线程都有自己的本地队列,... -
web测试中需要注意问题
2013-07-30 11:32:17web系统上线后,如果不是功能的bug,出现问题最多就是多线程并发,浏览器多窗口的问题 问题1: 可以保存重复数据,可以多次审批,发送? 重复数据:用户在多个窗口同时保存,或者系统保存后,网速太慢,多次... -
selenium自动化测试面试集合
2018-02-05 16:46:52Selenium脚本的执行速度受多方面因素的影响: 网速;操作步骤的繁琐程度;页面加载的速度;脚本中设置的等待时间;运行脚本的线程数;能稳定的视线回归测试是关键; 提高速度的方法: 减少操作步骤,如经过三... -
Android高性能加载大量图片系列课程2-在非UI线程中处理图片
2016-01-03 15:53:21如果加载的图片数据是从本地磁盘或者从远程服务器读取的话(也就是说除了从内存以外的任何地方读取图片数据),请不要在主线程(UI线程)中调用这些方法!为什么???因为从这些地方读区图片数据所花费的时间是不可... -
珠峰笔记(变量提升-闭包-this-OOP)第三周
2020-07-20 10:30:29NODE基础概念 node是工具或者环境 ...Ping 测网速 Ping www.baidu.com ipConfig (mac不支持) cls 清屏 (mac是clear) dir:查看当前目录下的所有文件(ls) mkdir 创建文件夹 rm xxx.xx 删除文件 rmdir xxx 删除文件 -
说两句
2018-11-23 20:56:10了解了下am3352 openwrt环境下QT4.8.3的交叉编译,以及QT代码的移植真心坑!!! openwrt 的LED,按键蜂鸣器的控制,应用更新,网速检测!... 用C语言多线程完成这些任务!!!! 还是要多了解下 提高下自己... -
【python】又拍云采集工具助手exe带python图片采集源码
2019-11-07 17:16:31自己测试了一下,没有用多线程,可能速度还是比较low! 写了报错以及记录功能,如果没有下载到的图片,自己手动补上吧,失败的链接都写在spider.txt上! 运行测试: 网速比较慢,暂时只有这么多了,程序应该可以... -
struts2-剩余2
2019-04-24 20:32:00多线程、Linux、云服务器、git spring springboot、springcloud 今晚:7:30 测试直播网速 一、回顾 拦截器:在目标action方法执行的前后插入一段额外的逻辑代码;aop面向切面编程的一种实现 实现Interceptor... -
[Python] 口令攻击实验
2013-10-18 19:59:45这是一段Python程序用于演示多线程口令攻击,可以自动把密码区间划分给不同的线程去攻击。并且在类定义中支持任务恢复功能。 它采用暴力方法,确实可以获得口令,但还是受到诸多限制:如计算机性能,网速,对方... -
爬行程序[ApacheLoader]
2007-10-28 17:05:00看看界面先写这个程序加深了一点体会分享下:1、多线程“加快了”程序运行速度,但遇到一些如与网速有关的情况时,并非线程开的越多越好。2、定义一些必要的接口增强了程序的可扩展性,如:用sql server 存储数据,... -
命令行运行java踩坑
2019-11-05 09:33:07为了模拟多人同时下载app,我开启多个线程去下载远程服务器上的apk文件,可是发现公司网速做了限制,最大只有2M/s,这样也就没法测出服务器实际的上传速度。于是,找同事借了一台电脑,两边同时下载。但是同事电脑上... -
油猴(Tampermonkey)插件+脚本+IDM=百度网盘高速下载
2018-01-29 09:56:00油猴(Tampermonkey)插件+脚本+IDM实现多线程高速下载。 我测试的是64位windows7系统,浏览器是Chrome。 1.下载、安装Chrome浏览器。 建议在安装时,安装在默认位置,我在使用其他一些软件调用浏览器的... -
python自动化常见问题汇总
2018-06-02 16:08:00Selenium脚本的执行速度受多方面因素的影响,如网速,操作步骤的繁琐程度,页面加载的速度,以及我们在脚本中设置的等待时间,运行脚本的线程数等。但是不能单方面追求运行速度的,要确保稳定性,能稳定地实现回归... -
如何提高selenium脚本的执行速度
2020-05-15 13:23:17Selenium脚本的执行速度受多方面因素的影响,如网速,操作步骤的繁琐程度,页面加载的速度,以及我们在脚本中设置的等待时间, 运行脚本的线程数等。所以不能单方面追求运行速度的,要确保稳定性,能稳定地实现回归... -
如何提高selenum的运行速度
2018-06-08 20:23:07selenum的运行速度会受到多方面的影响,如网速,操作步骤的繁琐性,页面的加载速度以及脚本中设置的等待时间,运行的线程数等,所以不能从单方面去追求速度。要确保稳定性,能稳定的实现回归测试超市关键。我们可以... -
vc++ 应用源码包_1
2012-09-15 14:22:12实现了自绘控件,云端控制主要在CnComm类多线程串口通讯库, camerads-DirectShow使用示例 演示了摄像头的使用 CatListBoxDemo ListBox控件与其它控件阙套使用方法 CCAMS系统是一种用于局域网下的CS模式的软件... -
vc++ 应用源码包_2
2012-09-15 14:27:40实现了自绘控件,云端控制主要在CnComm类多线程串口通讯库, camerads-DirectShow使用示例 演示了摄像头的使用 CatListBoxDemo ListBox控件与其它控件阙套使用方法 CCAMS系统是一种用于局域网下的CS模式的软件...