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();
        }
    }
     
     
  • 相关阅读:
    pytorch的常用接口、操作、注意事项
    pytorch 中conv1d操作
    NLP基本知识点和模型
    深度学习基础理论知识
    对交叉验证的理解
    阅读深度学习论文的一些技巧
    机器学习和深度学习入门总结
    架构思考-业务快速增长时的容量问题
    系统梳理一下锁
    稳定性五件套-限流的原理和实现
  • 原文地址:https://www.cnblogs.com/zhangmingda/p/14750606.html
Copyright © 2011-2022 走看看