当前位置:网站首页>附十七章 网络程序解读限定文章

附十七章 网络程序解读限定文章

2022-06-09 08:50:00 Maximize+

前言

这里的话,由于第十七的学习非常抽象,所以一时间很难理解,也无从下手导致一头雾水,这里为了照顾和自己更好掌握和理解,故诞生此文章!
有些程序的东西我也不清楚具体意思,我只能用更抽象更好的理解和记住这个内容的话来讲述,如有不对的地方欢迎指出!

下载网络资源的非断点下载程序解读

我们先看工具类的解读

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class DownUtil{
    
    //定义下载资源的路径
    private String path;
    //指定所下载文件的保存位置
    private String targetFile;
    //定义需要使用多少个线程来下载资源
    private int threadNum;
    //定义下载的线程对象
    private DownThread[] threads;
    //定义下载的文件的总大小
    private int fileSize;

    public DownUtil(String path,String targetFile,int threadNum){
    
        this.path=path;
        this.threadNum=threadNum;
        //初始化threads数组
        threads = new DownThread[threadNum];
        this.targetFile = targetFile;
    }
    public void download() throws Exception{
    
        var url = new URL(path);
        var conn = (HttpURLConnection)url.openConnection();
        conn.setConnectTimeout(5*1000);
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept","image/gif,image/jpeg,image/pjpeg,image/pjpeg,"+
                "application/x-shockwave-flash,application/xaml+xml,"+
                "application/vnd.ms-xpsdocument,application/x-ms-xbap,"+
                "application/x-ms-application,application.vnd.ms-excel,"+
                "application/vnd.ms-powerpoint,application/msword,*/*");
        conn.setRequestProperty("Accept-Language","zn-CN");
        conn.setRequestProperty("Charset","UTF-8");
        conn.setRequestProperty("Connection","Keep-Alive");
        //得到文件的大小
        fileSize = conn.getContentLength();
        conn.disconnect();
        int currentPartSize = fileSize / threadNum + 1;
        var file = new RandomAccessFile(targetFile,"rw");
        //设置本地文件的小
        file.setLength(fileSize);
        file.close();
        for(var i=0;i<threadNum;i++){
    
            //计算每个线程下载开始的位置
            var startPos = i * currentPartSize;
            //每个线程使用一个RandomAccessFile进行下载
            var currentPart = new RandomAccessFile(targetFile,"rw");
            //定位该线程下载的位置
            currentPart.seek(startPos);
            //创建下载线程
            threads[i]=new DownThread(startPos,currentPartSize,currentPart);
            threads[i].start();
        }
    }
    //获取下载的完成百分比
    public double getCompleteRate(){
    
        //统计多个线程已经下载的总大小
        var sumSize = 0;
        for(var i =0;i<threadNum;i++){
    
            sumSize += threads[i].length;
        }
        //返回已经完成的百分比
        return sumSize * 1.0/fileSize;
    }
    private class DownThread extends Thread{
    
        //当前线程下的下载位置
        private int startPos;
        //定义当前线程负责下载的文件大小
        private int currentPartSize;
        //当前线程需要下载的文件块
        private RandomAccessFile currentPart;
        //当前线程下载的字节数
        public int length;
        public DownThread(int startPos,int currentPartSize,RandomAccessFile currentPart){
    
            this.startPos=startPos;
            this.currentPartSize=currentPartSize;
            this.currentPart=currentPart;
        }
        @Override
        public void run() {
    
            try{
    
                var url  = new URL(path);
                var conn = (HttpURLConnection)url.openConnection();
                conn.setReadTimeout(5*1000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty(
                        "Accept","image/gif,image/jpeg,image/pjpeg,image.pjpeg,"+
                                "application/x-shockwave-flash,applicaton/xaml+xml,"+
                                "application/x-ms-application,application/vnd.ms-excel,"+
                                "application/vnd.ms-powerpoint,application/msword,*/*"
                );
                conn.setRequestProperty("Accept-Language","zh-CN");
                conn.setRequestProperty("Charset","UTF-8");
                InputStream inputStream = conn.getInputStream();
                //跳过startPos个字节,表明该线程只下载自己负责的那部分文件
                inputStream.skip(this.startPos);
                var buffer = new byte[5000];
                var hasRead = 0;
                //读取网络数据,并写入本地文件
                while(length<currentPartSize&&(hasRead = inputStream.read(buffer))!=-1){
    
                    currentPart.write(buffer,0,hasRead);
                    //累计该线程下载的总大小
                    length += hasRead;
                }
                currentPart.close();
                inputStream.close();

            }catch (Exception exception){
    
                exception.printStackTrace();
                System.out.println("程序遇到异常!");
            }
        }
    }
}

这里我们并不会截图全部程序内部的图片,但是根据复制代码,IDEA工具的行数是不变的,所以根据行数来看我的解读。
解读顺序大部分是按照从上向下来解读,解读内容也都是有关联的。
注:我们本章重点以行数来逐行解读
在这里插入图片描述

内容片段都是按照行数解读的,一定要看行数来对照!

程序①解读1

在这里插入图片描述

程序①解读2

在这里插入图片描述

程序①解读3

在这里插入图片描述

程序①解读4

在这里插入图片描述

程序①解读5

在这里插入图片描述

程序①解读6

在这里插入图片描述
在这里插入图片描述
其实乍一看这个工具类方法也没有什么复杂的,一拆分看不出什么特别难的,还有这里建议遇到不懂和没见过的类去翻译它的标识符名称或查阅Java API文档,或者去CSDN搜索关键词来有一个大概和详细的认知和学习!

public class MultiThreadDown {
    
    public static void main(String[] args) throws Exception{
    
        //初始化DownUtil对象
        final var dowmUtil = new DownUtil("https://pic2.zhimg.com/v2-7e82829cb4ed1598f80c089cd604e292_r.jpg","web.jpg",4);
        //开始下载

        dowmUtil.download();
        new Thread(()->{
    
            while(dowmUtil.getCompleteRate()<1){
    
                //隔隔0.1秒查询依次任务的完成进度
                //GUI程序中可根据该进度来绘制进度条
                System.out.println("已完成:"+dowmUtil.getCompleteRate());
                try{
    
                    Thread.sleep(100);
                }catch (Exception EX){
    
                }
            }
        }).start();
    }
}

这里是调用工具类的运行类
第一行填入参数 URL,下载本地的路径,和线程数量
然后调用download()方法表明启动线程工具类的核心内容,最后还创建了一个简易的用于获取下载进度的线程。

简单的控制台聊天室

服务器端

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/* 程序核心: 定义了一个线程安全的数组来保存ServerSocket套接字通过 accept()方法接收到的 客户端(Socket)对象 定义一个对应的 Socket s 来接收 accept()方法的返回值 然后直接进入 主机线程类里进行处理 */
public class MyServer {
    
    //定义保存所有Socket的ArrayList,并将其包装为线程安全的
    public static List<Socket>socketList = Collections.synchronizedList(new ArrayList<>());

    public static void main(String[] args) throws IOException {
    
        var ss = new ServerSocket(30000);
        while(true){
    
            //此行代码会阻塞,将一直等待别人的连接
            Socket s = ss.accept();
            socketList.add(s);
            new Thread(new ServerThread(s)).start();//这里我只启动一个客户端 所以就是一次性的线程 只有一个

        }
    }
}

服务器线程处理

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

/* 程序核心: 导入Runnable函数接口,然后定义了用来接收传进的Socket客户端对象的成员变量 定义了一个输入流 BufferedReader用于读取客户端的消息/信息,构造器进行赋值 br缓冲输入流的赋值为 该 s对象的 getInputStream方法 来获取对象的输入流 run()方法的实现核心逻辑: 定义一个String content变量用来作为读取 s对象输入流的内容 下面while的条件使用了方法的返回值来替代,而非直接是 br.readLine 虽然这样写法意义不大 但是也有专门的实现逻辑 通过 br.readLine()作为判断 返回值为String 如果有内容则返回内容,如果没有则直接返回null while检测到 content为null则直接停止循环 还实现了遍历客户端数组的所有数组并且使用 PrintStream处理流来包装 所有客户端的 OutputStream输出流 来输出 当前content遍历得到的所有内容 也就是说 这个类用于读取客户端发来的消息 并且将该客户端发来的消息全部发到其他客户端里 但是需要其他客户端读取 */
//负责处理每个线程通信的线程类
public class ServerThread implements Runnable{
    
    //定义当前线程所处理的Socket
    Socket s = null;
    //该线程所处理的Socket对应的输入流 缓冲输入流
    BufferedReader br = null;
    public ServerThread(Socket s)throws IOException{
    
        this.s=s;
        //初始化该Socket对应的输入流
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    }

    @Override
    public void run() {
    
        try{
    
            String content = null;
        /* 这里的 readFromClient()方法的实现逻辑挺有趣的,我也是第一次见到 如果br.readLine有数据就 return 返回值 返回值肯定是一个 String类型的 然后我们的 content再赋值一下 但是如果br.readLine没有数据了 就返回null 而如果 content等于null则停止循环 */
            while((content = readFromClient())!=null){
    
                //遍历SocketList中的每个Socket
                //将读到的内容向每个Socket发送因此
                //这里其实只是单个线程 所以下面的代码实际作用没有什么
                /* 这里的实现逻辑也非常简单,content字符串遍历了该Socket的所有OutputStream内容 直接遍历数组的所有 Socket然后获取它们的 OutputStream 然后直接 用一个 PrintStream将我们刚刚的content内容全部输入进去 只要接收到 content数据的Socket使用 BufferedReader方法来读取 OutputStream就会接收到 */
                for(var s : MyServer.socketList){
    
                    var ps = new PrintStream(s.getOutputStream());
                    ps.println(content);
                }
            }
        }catch (IOException ex){
    
            ex.printStackTrace();
        }
    }
    public String readFromClient(){
    
        try{
    
            return br.readLine();
        }catch (IOException e){
    //因为是单独的客户端所以我们不需要专门去数组里删除整个Socket对象
            e.printStackTrace();
        }
        return null;
    }
}

客服端

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
/* 程序核心: 客户端类的实现没有什么特别的 创建了一个用于连接服务器的 Socket对象 也是服务器端accept()方法的返回值 启动客户端的处理线程来处理Socket对象 并且创建了一个处理流来进行键盘输出处理 获取了 s的输出流 这里还创建了一个 BufferedReader键盘输入流 用来和服务器交互 */
public class MyClient {
    
    private static String clientName;
    public static void main(String[] args) throws Exception{
    
        var s = new Socket("127.0.0.1",30000);//用以连接服务器的 Socket对象 也是 服务器的 accept()方法返回的对象
        //客户端启动ClientThread线程不断地读取来自服务器的数据
        new Thread(new ClientThread(s)).start();
        //获取该Socket对应的输出流
        var ps = new PrintStream(s.getOutputStream());
        String line = null;
        //不断地读取键盘输入
        var br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请选择你的阵容:");
        System.out.println(" ");
        clientName = br.readLine();
        while((line = br.readLine())!=null){
    
            ps.println(clientName+" 说: "+line);
        }
    }
}

客户端线程处理

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/* 程序核心: 说实话客户端的线程实现类也没有什么特殊的 获取了Socket对象和创建了一个输入流 run()方法体实现逻辑: 读取Socket s对象的Output输出流并将其打印到输出台 */
public class ClientThread implements Runnable{
    
    //该线程负责处理的 Socket
    private Socket s;
    BufferedReader br = null;
    public ClientThread(Socket s) throws IOException {
    
        this.s=s;
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    }

    @Override
    public void run() {
    
        try{
    
            String content = null;
            while((content=br.readLine())!=null){
    
                System.out.println(content);
            }
        }catch (Exception e){
    
            e.printStackTrace();
        }
    }
}

如果无法开启多个客户端则去在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
勾选此选项。
这个程序的实习细节不难,而且也可以用Swing组件包装一下,但是很花费时间。

原网站

版权声明
本文为[Maximize+]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_55868059/article/details/124990511