zoukankan      html  css  js  c++  java
  • java 网络编程基础 InetAddress类;URLDecoder和URLEncoder;URL和URLConnection;多线程下载文件示例

    什么是IPV4,什么是IPV6:
    • IPv4使用32个二进制位在网络上创建单个唯一地址。IPv4地址由四个数字表示,用点分隔。每个数字都是十进制(以10为基底)表示的八位二进制(以2为基底)数字,例如:216.27.61.137。
    • IPv6使用128个二进制位在网络上创建一个唯一地址。IPv6地址由八组十六进制(以16为基数)数字表示,这些数字由冒号分隔,如2001:cdba:0000:0000:0000:0000:0000:3257:9652所示。为了节省空间,通常省略包含所有零的数字组,留下冒号分隔符来标记空白(如2001:cdba::3257:9652)。
    在IPv4地址出现之初,互联网还没有像今天这样引起商业轰动,大多数网络都是私有的,并且与世界上其他网络隔绝。当互联网爆发式增长,只有32位来识别一个独特的互联网地址感觉有点不够用,这让人们担心我们的IP地址会用完。在IPv4下,有232种可能的组合,提供了将近43亿个唯一地址。IPv6将其提升到2128亿个可能的组合。

    InetAddress

    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    
    /**
     * @ClassName InetAddressExample
     * @projectName: object1
     * @author: Zhangmingda
     * @description: XXX
     * date: 2021/5/9.
     */
    public class InetAddressExample {
        public static void main(String[] args) throws IOException {
            /**
             * 创建InetAddress 实例getByName静态方法
             */
            InetAddress ksyunAddr = InetAddress.getByName("www.ksyun.com");
            /**
             * 获取IP地址字符串
             */
            System.out.println(ksyunAddr.getHostAddress());
            /**
             * 判断地址是否可达:isReachable
             */
            System.out.println(ksyunAddr.isReachable(2000));
            /**
             * 获取本机IP InetAddress.getLocalHost()
             */
            InetAddress localIPObj = InetAddress.getLocalHost();
            System.out.println(localIPObj.getHostAddress());
        //
    //指定IP地址创建 InetAddress对象
    InetAddress localIp = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});

    } }

    URLDecoder和URLEncoder

    URLDecoder,URLEncoder用来完成普通字符串和applicationl/x-www-form-urlencoded MIME 字符串之间的相互转换
    比如我们在百度上搜索一个“李一桐”,wd对应的字符会变成:“%E6%9D%8E%E4%B8%80%E6%A1%90”,其实这个东西就是我们的 pplicationl/x-www-form-urlencoded MIME 字符串。
    public class UrlEncodeTest {
        public static void main(String[] args) throws UnsupportedEncodingException {
            String url = "https://www.baidu.com/狗逼/";
            //转码
            String encodeUrl = URLEncoder.encode(url,"utf-8");
            System.out.println(encodeUrl); //https%3A%2F%2Fwww.baidu.com%2F%E7%8B%97%E9%80%BC%2F
            //解码
            String str = URLDecoder.decode(encodeUrl,"utf-8");
            System.out.println(str);//https://www.baidu.com/狗逼/
    
        }
    }

    URL和URLConnection

    URL类提供了多个构造器用于创建URL对象,一旦获得URL对象之后,就可以调用如下方法来访问该URL对应的资源:
    • String getFile(): 获取该URL资源名。
    • String getHost(): 获取URL的主机名
    • String getPath(): 获取该URL的路径部分
    • int getPort(): 获取URL的端口号。
    • String getProtocol(): 获取该URL的协议名称
    • String getQuery() 获取该 URL 宇符串部分。
    • URLConnection openConnection(): 返回一个URLConnection它代表了与该URL 所引用的远对象的连接。
    • InputStream openStream() 打开与此URL连接,并返回一个用于读取该URL资源的 InputStream

    多线程下载文件示例:

    知识点:

    • URL类openConnection() 所引用的远对象的连接; 对象的getContentLength方法获取响应头文件字节长度
    • URL类openStream() 打开与此URL连接,并返回一个用于读取该URL资源的 InputStream
    • RandomAccessFile类创建文件、预置文件大小、从任意位置读写文件
    • 线程池:Executors.newCachedThreadPool() 获取ExecutorService线程池对象,submit 提交任务
    • 线程池任务类实现 Runnable 接口,重写run() 方法定义任务细节。

    设计思路:

    • 获取到文件大小,设置线程数,计算每个线程下载文件的初始字节数和下载(保存)字节数,每个线程从网络get到的字节覆盖写入到本地预置文件的对应字节位置内。

    代码示例(Range版):

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @ClassName ThreadPoolMultiDownLoad
     * @projectName: object1
     * @author: Zhangmingda
     * @description: 使用多线程线程池方式下载文件
     * date: 2021/5/9.
     */
    public class ThreadPoolMultiDownLoad {
        /**
         * 要下文件的url地址
         */
        private String urlString;
        /**
         * 线程数
         */
        private int threadNum;
        /**
         * 保存文件的目录
         */
        private String saveDir;
        /**
         * 文件的大小
         */
        private int fileSize;
        /**
         * 线程池
         */
        private static ExecutorService threadPool = null;
    
        /**
         * 下载类构造方法
         * @param urlString 文件地址
         * @param threadNum 线程数设置
         * @param saveDir 本地文件保存目录
         */
        public ThreadPoolMultiDownLoad(String urlString, int threadNum, String saveDir) {
            this.urlString = urlString;
            this.threadNum = threadNum;
            this.saveDir = saveDir;
        }
        /**
         * 创建线程池实例的方法,确保线程池为单例(多线程同时运行时使用同一个实例)
         */
        private static ExecutorService getThreadPool(){
            if (threadPool == null){
                synchronized (ThreadPoolMultiDownLoad.class){
                    if (threadPool == null){
                        threadPool = Executors.newCachedThreadPool();
                    }
                }
            }
            return threadPool;
        }
        /**
         * 获取文件大小方法
         */
        private int getFileSize(URL url) throws IOException {
            return url.openConnection().getContentLength();
        }
        /**
         * 并发用分块下载写入的类
         */
        private class DownPart implements Runnable {
            private int startByte;
            private int threadDownSize;
            private RandomAccessFile accessFile;
            private URL url;
    
            /** 本线程运行所需担心
             * @param startByte 下载起始字节
             * @param threadDownSize 本线程下载字节量
             * @param accessFile 本地写入文件
             */
            public DownPart(int startByte, int threadDownSize, RandomAccessFile accessFile,URL url) {
                this.startByte = startByte;
                this.threadDownSize = threadDownSize;
                this.accessFile = accessFile;
                this.url = url;
            }
            //本线程运行方法
            @Override
            public void run() {
                int readcount = 0;
                InputStream inputStream = null;
                //打开http输入流
                try {
                    URLConnection conn = this.url.openConnection();
                    /**
                     * 设置每个请求字节范围
                     */
                    conn.setRequestProperty("Range","bytes=" + startByte + "-" + (startByte + threadDownSize));
                    inputStream = conn.getInputStream();
    //                //跳过非本线程需要下载的字节
    //                inputStream.skip(startByte);
                    /**
                     * 将本线程需要下载的字节写到本地文件中
                     */
                    byte[] buffer = new byte[10240];
                    int len = 0;
                    while (readcount < this.threadDownSize && (len = inputStream.read(buffer)) != -1 ){
                        accessFile.write(buffer,0,len);
                        readcount += len;
                    }
                    System.out.println(Thread.currentThread().getName() + "下载写入了" + readcount + "字节的数据" );
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        if (inputStream != null){inputStream.close(); }
                        if (accessFile != null){accessFile.close();}
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }
        /**
         * 下载文件方法
         */
        public void download() throws IOException {
            /**
             * URL构造
             */
            URL url = new URL(this.urlString);
            /**
             * 文件大小获取
             */
            this.fileSize = getFileSize(url);
            /**
             * 每个线程下载的文件大小:由于fileSize总大小除以线程数量后,得出的小数部分会省略,所以每个线程多下载一字节
             */
            int threadDownSize = this.threadNum == 1 ? this.fileSize : (this.fileSize / this.threadNum + 1);
            /**
             * 获取文件名
             */
            String[] strings = this.urlString.split("/");
            String fileName = strings[strings.length-1];
            /**
             * 本地预创建文件并预置文件大小
             */
            String saveFilePath = this.saveDir + "/" + fileName;
            System.out.println(saveFilePath);
            RandomAccessFile accessFile = new RandomAccessFile(saveFilePath,"rw");
            accessFile.setLength(this.fileSize);
            accessFile.close();
            /**
             * 循环启动线程,分块写入到文件
             */
            url = new URL(urlString);
            for (int i=0; i<this.threadNum; i++){
                //文件起始写入的位置
                int startByte = i * threadDownSize;
                //打开文件将文件指针移到该线程应该覆盖写入本地文件字节位置
                RandomAccessFile randomAccessFile = new RandomAccessFile(saveFilePath,"rw");
                randomAccessFile.seek(startByte);
    
                getThreadPool().submit(new DownPart(startByte,threadDownSize,randomAccessFile,url));
            }
            getThreadPool().shutdown();
        }
    
    
        /**
         * @param args
         */
        public static void main(String[] args) throws IOException {
            String ks3utilUrl = "https://ks3-cn-beijing.ksyun.com/ks3-import/KS3-import-tool-2.1.4-dist.zip";
            new ThreadPoolMultiDownLoad(ks3utilUrl,4,"网络编程/src").download();
        }
    }
     
     
  • 相关阅读:
    Time Zone 【模拟时区转换】(HDU暑假2018多校第一场)
    HDU 1281 棋盘游戏 【二分图最大匹配】
    Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS换根 || 树形dp】
    Codeforces Round #527 (Div. 3) D2. Great Vova Wall (Version 2) 【思维】
    Codeforces Round #527 (Div. 3) D1. Great Vova Wall (Version 1) 【思维】
    Codeforces Round #528 (Div. 2, based on Technocup 2019 Elimination Round 4) C. Connect Three 【模拟】
    Avito Cool Challenge 2018 E. Missing Numbers 【枚举】
    Avito Cool Challenge 2018 C. Colorful Bricks 【排列组合】
    005 如何分析问题框架
    004 如何定义和澄清问题
  • 原文地址:https://www.cnblogs.com/zhangmingda/p/14750606.html
Copyright © 2011-2022 走看看