精华内容
下载资源
问答
  • 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 2500

    pthread_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类型的时候,是不是会影响运行速度?

     

     

     


     

     

     

    展开全文
  • 可以测网速的工具。
  • 工信部规定的网速测试标准 : 除普通网页测速采用单线程外,用户宽带接入速率测试应使用多线程(多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请求;

    -- 返回值 : 返回 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作为日常的部署语言,编写自动化脚本目前看来还是比较方便的,因为它的库...

    场景

    1. 使用Python做自动化测试时,有时候需要从网络下载软件安装包并安装。但是使用urllib库时,默认都是单线程下载文件,如果文件比较小还好说,如果文件有20M时,普通的网速就要等待很长的时间。有没有模块类似下载工具那样能多线程下载同一个文件?

    2. 如果没有多线程下载单个文件的模块,那我们应该如何编码实现功能?

    说明

    1. Python作为日常的部署语言,编写自动化脚本目前看来还是比较方便的,因为它的库很多,动态语言特性灵活,自动内存管理,编码到执行都无需编译等待等。

    2. 说到这个多线程下载单个文件,在Python的使用手册里,真没发现有相关的模块做这个功能。搜索了下也没简单能用的模块。

    3. 实现多线程下载同一个静态文件(注意是静态文件,而流式文件是获取不到大小的),原理就是每个线程下载文件的不同部分(一个文件可以看成不同大小的块组成),这样每个线程执行完之后,文件就全部下载完了。多线程下载速度也不一定是快的,要看下载网站的带宽出口,如果它的带宽出口比较小,那么多线程都会比单线程快。如果像腾讯那种大厂,它的网站带宽很大,还有DDOS检测防护,比你自己的网络带宽还大,所以基本上只用一个线程就很快了。

    4. 多线程下载文件的不同部分,首先需要发送HEAD请求获取http头里的Content-Length的文件大小,之后才能根据文件大小和线程个数分成多个块,每个块有起始位置和结束位置,而每个线程只下载自己的文件块就行了。

    5. 这里说明下,访问https和支持TLS协议,需要安装额外的模块,请查看关于如何使用urllib3库和访问https的问题,总的说需要先通过pip安装pyOpenSSL, cryptography, idna, certifi模块。

    pip install pyOpenSSL
    pip install cryptography
    pip install idna
    pip install certifi
    
    1. 之后如果想支持命令行参数管理,可以安装click模块。
    pip install click
    
    1. 最后就是需要设定一个下载缓存大小,我这里设置为100k。可以根据自己的网速设定,太大的话,http请求可能就会超过远程网站的返回大小导致速度很慢。之后还需要通过发送GET请求,附带请求头内容属性Range来获取文件指定范围的数据。当然如果某个线程负责下载的文件块过大,我们还需要分割为100k的子块,循环请求多次直到完成下载负责的文件块。
    headers = {"Range":"bytes=%d-%d"%(start,end)}
    res = self.http.request('GET',self.url,headers=headers,preload_content=False)
    
    1. 我这里的测试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独立文件

    1. 我使用pyinstaller打包了为单个独立的EXE文件, 如果没有Python环境的,可以从
      https://gitee.com/tobey-robot/AutomaticPython/releases下载MultiThreadDownloadFile.zip

    参考

    关于如何使用urllib3库和访问https的问题

    urllib3库的官方说明

    Python开发环境配置

    Simple Multithreaded Download Manager in Python

    python多线程下载文件

    展开全文
  • 工信部规定的网速测试标准 : 除普通网页测速采用单线程外,用户宽带接入速率测试应使用多线程(多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 ;

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. /** Runs the HTTP measurement task. Will acquire power lock to ensure wifi is not turned off */  
    2. @Override  
    3. public MeasurementResult call() throws MeasurementError {  
    4.     
    5.   int statusCode = HttpTask.DEFAULT_STATUS_CODE;  
    6.   long duration = 0;  
    7.   long originalHeadersLen = 0;  
    8.   long originalBodyLen;  
    9.   String headers = null;  
    10.   ByteBuffer body = ByteBuffer.allocate(HttpTask.MAX_BODY_SIZE_TO_UPLOAD);  
    11.   boolean success = false;  
    12.   String errorMsg = "";  
    13.   InputStream inputStream = null;  
    14.     
    15.   try {  
    16.     // set the download URL, a URL that points to a file on the Internet  
    17.     // this is the file to be downloaded  
    18.     HttpDesc task = (HttpDesc) this.measurementDesc;  
    19.     String urlStr = task.url;  
    20.           
    21.     // TODO(Wenjie): Need to set timeout for the HTTP methods  
    22.     httpClient = AndroidHttpClient.newInstance(Util.prepareUserAgent(this.parent));  
    23.     HttpRequestBase request = null;  
    24.     if (task.method.compareToIgnoreCase("head") == 0) {  
    25.       request = new HttpHead(urlStr);  
    26.     } else if (task.method.compareToIgnoreCase("get") == 0) {  
    27.       request = new HttpGet(urlStr);  
    28.     } else if (task.method.compareToIgnoreCase("post") == 0) {  
    29.       request = new HttpPost(urlStr);  
    30.       HttpPost postRequest = (HttpPost) request;  
    31.       postRequest.setEntity(new StringEntity(task.body));  
    32.     } else {  
    33.       // Use GET by default  
    34.       request = new HttpGet(urlStr);  
    35.     }  
    36.       
    37.     if (task.headers != null && task.headers.trim().length() > 0) {  
    38.       for (String headerLine : task.headers.split("\r\n")) {  
    39.         String tokens[] = headerLine.split(":");  
    40.         if (tokens.length == 2) {  
    41.           request.addHeader(tokens[0], tokens[1]);  
    42.         } else {  
    43.           throw new MeasurementError("Incorrect header line: " + headerLine);  
    44.         }  
    45.       }  
    46.     }  
    47.       
    48.       
    49.     byte[] readBuffer = new byte[HttpTask.READ_BUFFER_SIZE];  
    50.     int readLen;        
    51.     int totalBodyLen = 0;  
    52.       
    53.     long startTime = System.currentTimeMillis();  
    54.     HttpResponse response = httpClient.execute(request);  
    55.       
    56.     /* TODO(Wenjie): HttpClient does not automatically handle the following codes 
    57.      * 301 Moved Permanently. HttpStatus.SC_MOVED_PERMANENTLY 
    58.      * 302 Moved Temporarily. HttpStatus.SC_MOVED_TEMPORARILY 
    59.      * 303 See Other. HttpStatus.SC_SEE_OTHER 
    60.      * 307 Temporary Redirect. HttpStatus.SC_TEMPORARY_REDIRECT 
    61.      *  
    62.      * We may want to fetch instead from the redirected page.  
    63.      */  
    64.     StatusLine statusLine = response.getStatusLine();  
    65.     if (statusLine != null) {  
    66.       statusCode = statusLine.getStatusCode();  
    67.       success = (statusCode == 200);  
    68.     }  
    69.       
    70.     /* For HttpClient to work properly, we still want to consume the entire response even if 
    71.      * the status code is not 200  
    72.      */  
    73.     HttpEntity responseEntity = response.getEntity();        
    74.     originalBodyLen = responseEntity.getContentLength();  
    75.     long expectedResponseLen = HttpTask.MAX_HTTP_RESPONSE_SIZE;  
    76.     // getContentLength() returns negative number if body length is unknown  
    77.     if (originalBodyLen > 0) {  
    78.       expectedResponseLen = originalBodyLen;  
    79.     }  
    80.       
    81.     if (responseEntity != null) {  
    82.       inputStream = responseEntity.getContent();  
    83.       while ((readLen = inputStream.read(readBuffer)) > 0   
    84.           && totalBodyLen <= HttpTask.MAX_HTTP_RESPONSE_SIZE) {  
    85.         totalBodyLen += readLen;  
    86.         // Fill in the body to report up to MAX_BODY_SIZE  
    87.         if (body.remaining() > 0) {  
    88.           int putLen = body.remaining() < readLen ? body.remaining() : readLen;   
    89.           body.put(readBuffer, 0, putLen);  
    90.         }  
    91.         this.progress = (int) (100 * totalBodyLen / expectedResponseLen);  
    92.         this.progress = Math.min(Config.MAX_PROGRESS_BAR_VALUE, progress);  
    93.         broadcastProgressForUser(this.progress);  
    94.       }  
    95.       duration = System.currentTimeMillis() - startTime;  
    96.     }  
    97.                  
    98.     Header[] responseHeaders = response.getAllHeaders();  
    99.     if (responseHeaders != null) {  
    100.       headers = "";  
    101.       for (Header hdr : responseHeaders) {  
    102.         /* 
    103.          * TODO(Wenjie): There can be preceding and trailing white spaces in 
    104.          * each header field. I cannot find internal methods that return the 
    105.          * number of bytes in a header. The solution here assumes the encoding 
    106.          * is one byte per character. 
    107.          */  
    108.         originalHeadersLen += hdr.toString().length();  
    109.         headers += hdr.toString() + "\r\n";  
    110.       }  
    111.     }  
    112.       
    113.     PhoneUtils phoneUtils = PhoneUtils.getPhoneUtils();  
    114.       
    115.     MeasurementResult result = new MeasurementResult(phoneUtils.getDeviceInfo().deviceId,  
    116.         phoneUtils.getDeviceProperty(), HttpTask.TYPE, System.currentTimeMillis() * 1000,  
    117.         success, this.measurementDesc);  
    118.       
    119.     result.addResult("code", statusCode);  
    120.       
    121.     if (success) {  
    122.       result.addResult("time_ms", duration);  
    123.       result.addResult("headers_len", originalHeadersLen);  
    124.       result.addResult("body_len", totalBodyLen);  
    125.       result.addResult("headers", headers);  
    126.       result.addResult("body", Base64.encodeToString(body.array(), Base64.DEFAULT));  
    127.     }  
    128.       
    129.     Log.i(SpeedometerApp.TAG, MeasurementJsonConvertor.toJsonString(result));  
    130.     return result;      
    131.   } catch (MalformedURLException e) {  
    132.     errorMsg += e.getMessage() + "\n";  
    133.     Log.e(SpeedometerApp.TAG, e.getMessage());  
    134.   } catch (IOException e) {  
    135.     errorMsg += e.getMessage() + "\n";  
    136.     Log.e(SpeedometerApp.TAG, e.getMessage());  
    137.   } finally {  
    138.     if (inputStream != null) {  
    139.       try {  
    140.         inputStream.close();  
    141.       } catch (IOException e) {  
    142.         Log.e(SpeedometerApp.TAG, "Fails to close the input stream from the HTTP response");  
    143.       }  
    144.     }  
    145.     if (httpClient != null) {  
    146.       httpClient.close();  
    147.     }  
    148.       
    149.   }  
    150.   throw new MeasurementError("Cannot get result from HTTP measurement because " +   
    151.     errorMsg);  
    152. }    



    二. 分析源码中用到的 API 


    1. HttpClient


    (1) HttpClient 接口


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


    接口方法 : 执行 各种 HttpRequest, 获取连接管理实例 , 获取客户端参数; 


    (2) AndroidHttpClient 类


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


    方法介绍 : 

    execute(HttpUriRequest) : 

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. 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 服务器的重定向请求, 这个行为可以通过调用 setFollowRedirects(false) 关闭;


    (3) HttpPost 类


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


    Post方法功能 : 

    -- 注释资源 : 给存在的资源添加注释;

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

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

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


    (4) HttpHead 类


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


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


    方法作用 : 这个方法可以用来获取 请求中的元信息, 而不会获取 请求数据; 


    常用用途 : 检验超文本的可用性, 可达性, 和最近的修改;



    3. HttpResponse 


    (1) HttpResponse 接口


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


    方法介绍 

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. public abstract HttpEntity getEntity ()  
    -- 获取响应环境 : 根据环境确定 响应码对应的原因;

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. public abstract Locale getLocale ()  
    -- 获取状态行 : 获取响应的状态行

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. public abstract StatusLine getStatusLine ()  
    -- 设置响应实体 : 

    -- 设置响应环境 : 

    -- 设置状态行 : 

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. public abstract void setReasonPhrase (String reason)  
    -- 设置状态码 : 更新状态码, 状态码只能更新, 不能显示的设置 或者在构造方法中设置;

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. 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)方法, 创建该实例, 创建实例的时候, 传入的字符串是 包名 + 版本号, 自己组织;

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. AndroidHttpClient  httpClient = AndroidHttpClient.newInstance(packageName + " , " + version);  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. HttpRequestBase request = null;  
    2. if (task.method.compareToIgnoreCase("head") == 0) {  
    3.   request = new HttpHead(urlStr);  
    4. else if (task.method.compareToIgnoreCase("get") == 0) {  
    5.   request = new HttpGet(urlStr);  
    6. else if (task.method.compareToIgnoreCase("post") == 0) {  
    7.   request = new HttpPost(urlStr);  
    8.   HttpPost postRequest = (HttpPost) request;  
    9.   postRequest.setEntity(new StringEntity(task.body));  
    10. else {  
    11.   // Use GET by default  
    12.   request = new HttpGet(urlStr);  
    13. }  


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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. byte[] readBuffer = new byte[HttpTask.READ_BUFFER_SIZE];  
    2. int readLen;        
    3. int totalBodyLen = 0;  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. HttpResponse response = httpClient.execute(request);  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. StatusLine statusLine = response.getStatusLine();  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. if (statusLine != null) {  
    2.   statusCode = statusLine.getStatusCode();  
    3.   success = (statusCode == 200);  
    4. }  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. HttpEntity responseEntity = response.getEntity();   

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. originalBodyLen = responseEntity.getContentLength();  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. InputStream inputStream = responseEntity.getContent();  

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. readLen = inputStream.read(readBuffer)  


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


    作者 : 万境绝尘  

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

    展开全文
  • 一款流行的网络速度测试工具,线路遍布全球,包括非常多的中国线路,最近更新了多线程测速,以及在 Android 版本上添加了运营商信号地图覆盖功能,能够直接在地图上看到网络信号覆盖情况。今日关键词测网速如果你想...
  • 上一篇我写了如何使用 java 结合网络连接+多线程+RandomAssessFile类实现多线程切片下载并显示网速https://blog.csdn.net/yali_aini/article/details/81942036 因为之前写的都是客户端,不需要去管服务端,直接把...
  • 编写多线程并发的测试类

    千次阅读 2017-09-24 11:27:21
    在实际运用的过程中,我们经常遇到一些...2.一个下订单接口,用户要是操作速度或者网速的原因导致两个请求同时到达服务器,难道就插入两条订单么?同样,说不定还得邮件反省。 事由: 1.集群分布式部署导致synchroni
  • 线程执行过程中,有很场景需要终止线程的运行。这时就需要一个方法来结束线程并返回结果。 中断的方法 Thread类中提供了3个有关中断的方法: interrupt():设置中断标志位为true interrupted();检测中断标志,...
  • 此是自己优化多核多线程测试用的一个基准程序,是一个udp 的iocp实现,不具多少实用性,但具有研究性.采用分布式队列思想,尽量减少因同步而导致的线程切换带来的开销.在程序中采用tls,使每个线程都有自己的本地队列,...
  • web系统上线后,如果不是功能的bug,出现问题最多就是多线程并发,浏览器多窗口的问题 问题1: 可以保存重复数据,可以多次审批,发送? 重复数据:用户在多个窗口同时保存,或者系统保存后,网速太慢,多次...
  • selenium自动化测试面试集合

    千次阅读 2018-02-05 16:46:52
    Selenium脚本的执行速度受方面因素的影响: 网速;操作步骤的繁琐程度;页面加载的速度;脚本中设置的等待时间;运行脚本的线程数;能稳定的视线回归测试是关键; 提高速度的方法: 减少操作步骤,如经过三...
  • 如果加载的图片数据是从本地磁盘或者从远程服务器读取的话(也就是说除了从内存以外的任何地方读取图片数据),请不要在主线程(UI线程)中调用这些方法!为什么???因为从这些地方读区图片数据所花费的时间是不可...
  • NODE基础概念 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语言多线程完成这些任务!!!! 还是要多了解下 提高下自己...
  • 自己测试了一下,没有用多线程,可能速度还是比较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实现多线程高速下载。  我测试的是64位windows7系统,浏览器是Chrome。  1.下载、安装Chrome浏览器。  建议在安装时,安装在默认位置,我在使用其他一些软件调用浏览器的...
  • Selenium脚本的执行速度受方面因素的影响,如网速,操作步骤的繁琐程度,页面加载的速度,以及我们在脚本中设置的等待时间,运行脚本的线程数等。但是不能单方面追求运行速度的,要确保稳定性,能稳定地实现回归...
  • Selenium脚本的执行速度受方面因素的影响,如网速,操作步骤的繁琐程度,页面加载的速度,以及我们在脚本中设置的等待时间, 运行脚本的线程数等。所以不能单方面追求运行速度的,要确保稳定性,能稳定地实现回归...
  • selenum的运行速度会受到方面的影响,如网速,操作步骤的繁琐性,页面的加载速度以及脚本中设置的等待时间,运行的线程数等,所以不能从单方面去追求速度。要确保稳定性,能稳定的实现回归测试超市关键。我们可以...
  • 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模式的软件...

空空如也

空空如也

1 2 3
收藏数 44
精华内容 17
关键字:

多线程测网速