美文网首页
网络编程(3)——网络基础文件下载,多线程下载

网络编程(3)——网络基础文件下载,多线程下载

作者: 让时间走12138 | 来源:发表于2020-05-16 17:37 被阅读0次

本节内容

1.OSI文件模型介绍

2.HTTP请求和响应报文结构

3.IP地址、DNS和端口号

4.使用URLConnection获取网络数据

5.实现下载文件

6.多线程下载获取文件

7.多线程下载结构分析

8.分配线程下载任务

9.实现进度监听

一、OSI文件模型介绍

1.网络中传递数据的过程:客户端(发起请求)——>本机(对请求的数据进行包装)——>(TCP/IP协议进行传输)网络中传输——>服务器主机(通过端口号访问具体进程)——>进程
2.OSI的七层网络数据模型
  • (1)应用层:为操作系统或网络程序提供访问网络服务器的接口。使用URLConnection这个接口或者socket发起网络申请
  • (2)表示层:提供数据格式转换服务,加密和解密,图片解码和编码,数据压缩和解压
  • (3)会话层:建立一个端到端的链接,并提供访问验证和会话管理
  • (4)传输层:提供应用进程之间的逻辑通信,建立连接,数据校验,数据包次序,如TCP,UDP,进程,端口
  • (5)网络层:为数据在节点之间的传输创建逻辑链路,并分组转发数据
  • (6)数据链路层:在通信实体间建立数据链路连接,如数据分帧,处理流控制,物理地址寻址,重发
  • (7)物理层:负责最后将信息编码成电流脉冲或者其他信号用于网上传输
3.OSI的四层网络数据模型
  • (1)应用层:HTTP,FTP,DNS,SMTP
  • (2)传输层:通过TCP,UDP协议进行传输
  • (3)网络层:数据包装,寻址路由IP、PHP、ICMP协议
  • (4)物理层
4.html与服务器建立联系的过程:html 通过操作系统提供的接口发起请求——>将请求的数据使用TCP/IP协议进行包装——>网络层进行数据分包——>链路中——>物理层——————————>网络————————>主机(物理层—>链路层—>网络层—>传输层—>应用层)

二、HTTP请求和响应报文结构

1.Http和TCP/UDP 的关系:
  • HTTP协议 :主要负责数据的具体传输,对数据进行封装
  • TCP/UDP协议 :负责将数据在网络中传输
2.请求Request和响应Response
  • 请求:
  • 客户端需要向服务器上传数据
  • 客户端需要从服务器下载数据
  • 响应:
  • 服务器端对客户端的请求作出的回应
3.三次握手建立连接
  • HTTP封装完数据后,需要将数据使用TCP协议向网络中的其他主机(服务器)进行发送,需要经过三个过程/三次握手
4.Http协议报文
  • 请求方式:
  • GET:请求数据(1.提交数据 2.接收返回的数据)提交的数据会在url地址中显示表示出来(大小有限制)
  • POST:请求数据(1.提交数据 2.接收返回的数据)提交的数据在请求体重 url地址不会出现(有文件就必须使用POST)
  • HEAD:只是获取服务器端返回的响应信息,不会获取具体的内容,先获取大小,再分配线程
  • 状态码:
  • 200-206 请求成功
  • 300-305 重定向 www.baidu.com->www.qq.com(就是进入另外一个网址)
  • 400-405 客户端错误
  • 500-505 服务器端错误
  • 请求头:请求的方式、地址,上传的参数 。请求体:提交的是什么里面就是什么。
  • 响应头:回调给我们的数据,包含一些状态码和一些资源信息。响应体:包括具体的资源。

三、IP地址、DNS和端口号

1.IP地址:就是用来唯一标识网络中的一台设备(192.10.121这种类型)
2.域名:www.baidu.com
3.DNS:域名解析器。可以通过域名解析出它的IP地址
4.一个网址http://127.0.0.1/login.php?user_name=jack&user_pwd=123
  • http:表示数据传输使用的具体协议
  • 127.0.0.1:访问的主机地址
  • login.php:表示访问主机的文件或者目录
  • ?(分隔符):表示需要向服务器提交数据,提交方式GET。后面就是具体提交的数据。
  • user_name=:表示提交的数据,user_name是服务器定义的字段,jack是字段对应的数据
  • 使用&来链接多个字段
5.端口号
  • 端口对应的是一种服务
  • 80端口对应的是网络服务
  • 公认端口:0-1023 一些特定的服务
  • 注册端口:1024-49151 应用程序使用该范围端口
  • 动态私有端口:49152-65535

四、使用URLConnection获取网络数据

1.Java使用URL来封装网络数据的地址
2.url地址里面不能包含中文或者其他一些特殊的字符,对于这些字符需要进行编码或者解码
使用方法如下
public class MyClass {
    public static void main(String[] args) throws Exception {
        String str ="http://127.0.0.1/login.php?user_name=jack&user_pwd=123&
submit=%E7%99%BB%E5%BD%95";
        URL url =new URL(str);

       String word= URLDecoder.decode("%E7%99%BB%E5%BD%95","utf-8");
       String code=URLEncoder.encode("登录","utf-8");
       System.out.println(code);
      System.out.println(word);
    }
}
3.通过以下方式获取主机地址
System.out.println(url.getHost());
4.通过以下方式获取使用的协议
System.out.println(url.getProtocol());
5.通过以下方式获取具体提交的数据
System.out.println(url.getQuery());
6.向网络中发起请求
  • (1).获取对应的url地址
String str ="http://127.0.0.1/login.php?user_name=jack&user_pwd=123";
        URL url =new URL(str);
  • (2).使用URLConnection发起链接
HttpURLConnection coon=(HttpURLConnection)url.openConnection();
        coon.setRequestMethod("GET");
        coon.setDoInput(true);/设置是否打开接收的流
        coon.setDoOutput(true);/设置是否打开输出流,POST必须打开
        coon.setConnectTimeout(5*1000);/链接请求时间
        coon.setUseCaches(false);/不需要缓存
  • (3).数据接收和发送都是使用输入输出流
        /url.openStream()从服务器端获取数据
        /coon.getOutputStream();向服务器端发送数据
      InputStream is= url.openStream();
  • (4).处理输入流的数据
byte[] buffer= new byte[1024];
      int len=0;
      while((len=is.read(buffer))!=-1){
          String content= new String(buffer,0,len);
          System.out.println(content);
      }
  • (5).关闭流
is.close();

五、实现下载文件

1.新建一个URL对象
URL url=new URL("http://127.0.0.1/upload/vedio/test.mp4");
2.连接
HttpURLConnection coon=(HttpURLConnection)url.openConnection();
3.获取输入流
InputStream inputStream=url.openStream();
BufferedInputStream bis=new BufferedInputStream(inputStream);
4.创建输出流
        String des= "C:\\Users\\86178\\Desktop\\gg.mp4";
        FileOutputStream fos=new FileOutputStream(des);
        BufferedOutputStream bos= new BufferedOutputStream(fos);
5.将数据写入磁盘
        byte []  buffer= new byte[1024];
        int len=0;
        while ((len=bis.read(buffer))!=-1){
            bos.write(buffer,0,len);
        }
        bos.close();
        fos.close();
        bis.close();
        inputStream.close();

六、多线程下载获取文件

1.创建一个DownloadManager类,并采用单例设计模式
public class DownloadManager {
    private Map<String,String>[] source;
    private static DownloadManager manager;
    private DownloadManager(){}
    private static Object obj=new Object();

    public static  DownloadManager getInstance(){
        if(manager==null){
            synchronized (obj){
                if (manager==null){
                    manager=new DownloadManager();
                }
            }
        }
        return manager;
    }
    public void loadData(String urlString,String filePath){
        DownOperation  downloader= new DownOperation(urlString,filePath,3);
        downloader.download();
    }
}
2.创建一个DownOperation类,创建它的构造方法,并提供获取文件大小以及链接数据的函数
public class DownOperation {
    private URL url;
    private String filePath;
    private int threadCount;
    private DownThread [] tasks;
    private long size;

    public DownOperation(String urlString,String filePath, int threadCount)  {
        try {
            this.url=new URL(URLEncoder.encode(urlString,"utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.filePath = filePath;
        this.threadCount = threadCount;
      tasks= new DownThread[threadCount];
    }
  public void download(){
         //获取文件的大小
         getFileSize();
         System.out.println(size);
  }
  private void getFileSize(){
     //获取连接
      HttpURLConnection coon=null;
      try {
          coon=(HttpURLConnection) url.openConnection();
          coon.setRequestMethod("HEAD");
          //获取资源大小
         size= coon.getContentLengthLong();

      } catch (IOException e) {
          e.printStackTrace();
      }finally {
          //关闭链接
          coon.disconnect();
      }

  }
}
3.在主函数中,直接通过DownloadManager实现文件的下载
public class MyClass {
    public static void main(String[] args) {
        String url="http\\127.0.0.1\\upload\\video\\test.mp4";
        String filePath="C:\\Users\\86178\\Desktop";
        //下载一个文件
     DownloadManager.getInstance().loadData(url,filePath);
    }
}

七、多线程下载结构分析

1.首先需要知道文件大小->知道下载线程数量->准备文件接收数据->通过RandomAccessFile访问数据

八、分配线程下载任务

1.对前面的代码进行改写,在download函数里面添加一些内容
public void download(){
         //获取文件的大小
         getFileSize();
        //创建文件,用于保存下载的数据
      createFile();
      //分配线程下载数据
      dispatch();
  }
2.分别实现后面两个函数,这一切都在DownOperation类里面
private void dispatch(){
        //计算每个线程下载的平均值
      long average=size/threadCount;
      long start =0;
      long donwloadsize=average;
      for(int i=0;i<threadCount;i++){
         start=i*average;
         //最后一个线程,可能下载的数量要大于平均值
          if(i==threadCount-1){
              donwloadsize=size-start;
          }
          //创建线程,执行对应的模块进行下载
          DownThread dt =new DownThread(url,filePath,start,donwloadsize);
          //添加保存线程对象
          tasks[i]=dt;
          //启动下载
          dt.start();

      }
  }

  private void createFile(){
      File file =new File(filePath);
      try {
          file.createNewFile();
      } catch (IOException e) {
          e.printStackTrace();
      }

      //设置文件大小
      RandomAccessFile rac=null;
      try {
          rac= new RandomAccessFile(file,"rw");
          rac.setLength(size);
      } catch (Exception e) {
          e.printStackTrace();
      }finally {
          try {
              rac.close();
          } catch (IOException e) {
              e.printStackTrace();
          }

      }
  }
3.创建一个DownThread类,在它的run方法里面真正实现文件的多线程下载
public class DownThread extends Thread {
    private URL url;
    private String filePath;
    private long startPosition;
    private long size;
    private long downloadedLength;
   public DownThread (URL url,String filePath,long startPosition,long size){
       this.url=url;
       this.filePath=filePath;
       this.startPosition=startPosition;
       this.size=size;
   }

    @Override
    public void run() {
       //定位文件到这个线程应该写入的位置
        try {
            RandomAccessFile raf=new RandomAccessFile(filePath,"rw");
            raf.seek(startPosition);

            //开始下载
            HttpURLConnection coon=(HttpURLConnection) url.openConnection();
            coon.setRequestMethod("GET");
           coon.setDoInput(true);
           coon.setConnectTimeout(5*1000);

           //获取输入流
            InputStream is= url.openStream();
          //设置数据读取位置
            is.skip(startPosition);

            //开始读取数据,写入到文件
            byte[]  buffer=new byte[1024];
            int len=0;
            while ((len=is.read(buffer))!=-1){
                raf.write(buffer,0,len);
                //记录当前下载长度
                downloadedLength+=len;
              //判断当前线程下载的范围是否结束
                if(downloadedLength>size){
                    break;
                }
            }
            is.close();
            coon.disconnect();
            raf.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
其他的和第六的代码一样

九、实现进度监听

1.在DownOperation类里面添加一个currentRate方法来记录当前的下载进度
public float currentRate(){
        long len=0;
        for(DownThread dt:tasks){
           len=dt.downloadedLength;
        }
        return (float)len/size;
  }
2.在DownloadManager类里面的loadData函数里面,new 一个Thread对象,然后实现runnable接口
public void loadData(String urlString,String filePath){
    final DownOperation  downloader= new DownOperation(urlString,filePath,3);
        downloader.download();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    float rate=downloader.currentRate();
                    if(rate<1.0000001) {
        System.out.println("当前下载比例:" + downloader.currentRate());
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        break;
                    }
                }
            }
        }).start();
    }
OK,以上就是今天的全部内容了,再见。

相关文章

网友评论

      本文标题:网络编程(3)——网络基础文件下载,多线程下载

      本文链接:https://www.haomeiwen.com/subject/myyvghtx.html