Java-Socket编程基于TCP协议实现文件的下载

        作为一个初学者,最近在开始学习Java语言,该说不说,学习一门编程语言确实是有点枯燥,哈哈哈哈。每天就是看视频,敲代码,反反复复,唉。所以为了增加点乐趣,写个小程序,总结下这两天学到的东西。

        整个程序思路很简单,就是:客户端获取到服务端可下载的文件,选择其中一个进行下载。

        实现过程:

        服务端

                1、获取指定文件夹下的所有文件对象(使用listFile),存入集合。

    /*递归获取服务端指定文件夹下所有可供下载的文件
     * 存到集合中,将此集合返回*/
    public static ArrayList<File> getFiles(File e) {
        File[] list = e.listFiles();
        for (File x : list) {
            if (x.isDirectory()) {
                getFiles(x);
            } else if (!x.isDirectory()) {
                fileList.add(x);
            }
        }
        return fileList;
    }

                2、将集合序列化,存入本地,生成txt文件。

        ObjectOutputStream write = new ObjectOutputStream(new FileOutputStream("SocketPrj\\src\\FileDownLoad\\服务端配置文件.txt"));
        write.writeObject(fileList);    //序列化集合对象,将集合对象发送到客户端然后反序列化,然后展示可以下载的文件

                3、将序列化后的文件发送至客户端,这样就可以每次运行程序,客户端就可以获取到最新的服务端可供下载的所有的文件。当然,这只是其中的一种实现方式,主要是想复习下序列化和反序列化。

        conSocket = serverSocket.accept();
        System.out.println("连接成功,开始发送文件列表...");
        OutputStream output = conSocket.getOutputStream();
        FileInputStream getSetFile = new FileInputStream("SocketPrj\\src\\FileDownLoad\\服务端配置文件.txt");   //准备读取集合序列化文件
        byte[] bt = new byte[1024];
        int len = 0;
        while ((len = getSetFile.read(bt)) != -1) {
            output.write(bt, 0, len);
        }
        conSocket.shutdownOutput();
        write.close();
        System.out.println("配置文件发送完成");

                4、接收客户端请求下载的文件,然后向客户端发送指定的文件。

        fileSocket = serverSocket.accept();     //新开的负责文件发送的socket
        System.out.println("客户端选择下载的文件是:" + downLoadFileName + ",开始发送...");
        for (File x : fileList) {
            if (downLoadFileName.equals(x.getName())) {
                FileInputStream readFile = new FileInputStream(x);
                BufferedInputStream bfReader = new BufferedInputStream(readFile);   //读取将发送的本地文件
                OutputStream out = fileSocket.getOutputStream();
                byte[] bt = new byte[1024];     //设置每次发送数据的大小
                int len = 0;
                while ((len = bfReader.read(bt)) != -1) {
                    out.write(bt, 0, len);
                }
                fileSocket.shutdownOutput(); //通知客户端文件已发送完毕,关闭读取
                break;
            }
        }

        客户端

                1、反序列化服务端发来的txt文件,获取到可下载的文件对象并且展示到控制台。

        //本地配置文件,存储的是可供下载的文件对象
        FileOutputStream writeSetFile = new FileOutputStream("SocketPrj\\src\\FileDownLoad\\客户端配置文件.txt");
        //接收服务端发来的配置文件
        BufferedOutputStream bfWriterSetFile = new BufferedOutputStream(writeSetFile);
        conSocket = new Socket(InetAddress.getLocalHost(), 8848);
        System.out.println("连接成功,正在接收配置文件(文件集合)");
        InputStream in = conSocket.getInputStream();
        byte[] bt = new byte[1024];
        int len = 0;
        while ((len = in.read(bt)) != -1) {
            bfWriterSetFile.write(bt, 0, len);
            bfWriterSetFile.flush();
        }
        bfWriterSetFile.close();
        System.out.println("获取成功!");
        //返序列化文件,获取存储的文件对象
        ObjectInputStream getSetFiles = new ObjectInputStream(new FileInputStream("SocketPrj\\src\\FileDownLoad\\客户端配置文件.txt"));
        Object obj = getSetFiles.readObject();
        ArrayList<File> list = (ArrayList<File>) obj;
        getSetFiles.close();
        ArrayList<String> tempList = new ArrayList<>();
        System.out.println("服务器可供下载文件有:");
        for (File x : list) {
            tempList.add(x.getName());
            System.out.println(x.getName());
        }

                2、根据展示内容选择要下载的文件,发送下载请求。

        /*接收用户输入的想下载的文件,输入不存在的文件名
         * 就会一直处于输入状态
         * 输入正确跳出循环
         * 正确后向服务端发送下载请求*/
        while (true) {
            System.out.print("输入你想下载的文件名:");
            str = new Scanner(System.in).nextLine();
            if (tempList.contains(str)) {
                System.out.println("输入正确,等待下载...");
                break;
            } else {
                System.out.println("你输入的文件不存在!请重新输入:");
            }
        }
        /*向服务器发送下载请求*/
        OutputStream out = conSocket.getOutputStream();
        out.write(str.getBytes());
        conSocket.shutdownOutput();
        System.out.println("请求发送成功,等待下载...");
        conSocket.close();

                3、接收服务端发来的文件,存入本地文件夹。

        //因为第一个负责通信的socket使用了shutdownOutput,导致无法使用其继续发送数据
        //所以新开一个socket专门负责发文件
        fileSocket = new Socket(InetAddress.getLocalHost(), 8848);
        System.out.println("开始下载文件...");
        FileOutputStream writer = new FileOutputStream("SocketPrj\\ClientFiles\\" + str);
        InputStream in = fileSocket.getInputStream();
        byte[] bt = new byte[1024];
        int len;
        while ((len = in.read(bt)) != -1) {
            writer.write(bt, 0, len);
        }
        System.out.println("下载成功!");

服务端完整代码:

package FileDownLoad;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
/*文件下载之服务端
 * 作者:小刘同学
 * 时间:2021年10月21日 23点28分
 * 目的:复习巩固下socket编程
 * */
public class ServerAllFiles extends Thread {
    static ArrayList<File> fileList = new ArrayList<>();        //存储可供下载文件对象的集合
    static ServerSocket serverSocket;   //服务端Socket,建立监听对象
    static String downLoadFileName = null;  //接收客户端发过来的文件名
    static Socket conSocket = null;     //负责和客户端通信的socket
    static Socket fileSocket = null;    //接收文件的socket

    public static void main(String[] args) throws IOException, InterruptedException {
        ServerAllFiles th = new ServerAllFiles();
        th.start();
    }

    @Override
    public void run() {
        File allFiles = new File("SocketPrj\\ServerAllFiles");
        getFiles(allFiles);
        try {
            sendSetFile();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /*递归获取服务端指定文件夹下所有可供下载的文件
     * 存到集合中,将此集合返回*/
    public static ArrayList<File> getFiles(File e) {
        File[] list = e.listFiles();
        for (File x : list) {
            if (x.isDirectory()) {
                getFiles(x);
            } else if (!x.isDirectory()) {
                fileList.add(x);
            }
        }
        return fileList;
    }

    /*发送配置文件(里面装的是可供下载文件的名字)*/
    public static void sendSetFile() throws IOException, InterruptedException {
        ObjectOutputStream write = new ObjectOutputStream(new FileOutputStream("SocketPrj\\src\\FileDownLoad\\服务端配置文件.txt"));
        write.writeObject(fileList);    //序列化集合对象,将集合对象发送到客户端然后反序列化,然后展示可以下载的文件
        System.out.println("列表更新完成");
        serverSocket = new ServerSocket(8848);
        System.out.println("正在等待客户端连接...");

        conSocket = serverSocket.accept();
        System.out.println("连接成功,开始发送文件列表...");
        OutputStream output = conSocket.getOutputStream();
        FileInputStream getSetFile = new FileInputStream("SocketPrj\\src\\FileDownLoad\\服务端配置文件.txt");   //准备读取集合序列化文件
        byte[] bt = new byte[1024];
        int len = 0;
        while ((len = getSetFile.read(bt)) != -1) {
            output.write(bt, 0, len);
        }
        conSocket.shutdownOutput();
        write.close();
        System.out.println("配置文件发送完成");
        System.out.println("等待接收下载请求...");
        InputStream in = conSocket.getInputStream();
        byte[] btRecive = new byte[1024];
        int lenRecive = 0;
        downLoadFileName = null;     //接收客户端发过来的下载文件名
        while ((lenRecive = in.read(btRecive)) != -1) {
            downLoadFileName = new String(btRecive, 0, lenRecive);
        }
        conSocket.close();
        recivePost();
    }

    /*接收客户端下载请求
     * 发送文件*/
    public static void recivePost() throws IOException {
        fileSocket = serverSocket.accept();     //新开的负责文件发送的socket
        System.out.println("客户端选择下载的文件是:" + downLoadFileName + ",开始发送...");
        for (File x : fileList) {
            if (downLoadFileName.equals(x.getName())) {
                FileInputStream readFile = new FileInputStream(x);
                BufferedInputStream bfReader = new BufferedInputStream(readFile);   //读取将发送的本地文件
                OutputStream out = fileSocket.getOutputStream();
                byte[] bt = new byte[1024];     //设置每次发送数据的大小
                int len = 0;
                while ((len = bfReader.read(bt)) != -1) {
                    out.write(bt, 0, len);
                }
                fileSocket.shutdownOutput(); //通知客户端文件已发送完毕,关闭读取
                break;
            }
        }
        System.out.println("发送完成,客户端已接收到文件!");
    }
}

客户端完整代码

package FileDownLoad;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;

public class ClientDownCls extends Thread {
    /*文件下载之客户端
     * 作者:小刘同学
     * 时间2021年10月21日 23点28分
     * 目的:复习巩固下socket编程
     * */
    static Socket conSocket;
    static Socket fileSocket;
    static String str = null;

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ClientDownCls client = new ClientDownCls();
        client.start();
    }

    @Override
    public void run() {
        try {
            getSetFile();   //其中包含展示可下载的文件列表,以及下载文件
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /*接收服务端传过来的配置文件(文件集合)*/
    public static void getSetFile() throws IOException, ClassNotFoundException {
        //本地配置文件,存储的是可供下载的文件对象
        FileOutputStream writeSetFile = new FileOutputStream("SocketPrj\\src\\FileDownLoad\\客户端配置文件.txt");
        //接收服务端发来的配置文件
        BufferedOutputStream bfWriterSetFile = new BufferedOutputStream(writeSetFile);
        conSocket = new Socket(InetAddress.getLocalHost(), 8848);
        System.out.println("连接成功,正在接收配置文件(文件集合)");
        InputStream in = conSocket.getInputStream();
        byte[] bt = new byte[1024];
        int len = 0;
        while ((len = in.read(bt)) != -1) {
            bfWriterSetFile.write(bt, 0, len);
            bfWriterSetFile.flush();
        }
        bfWriterSetFile.close();
        System.out.println("获取成功!");
        getList();
    }

    /*读取配置文件,获取集合对象
     * 选择下载文件,将用户选择的下载的文件的请求发回服务器
     * */
    public static void getList() throws IOException, ClassNotFoundException {
        //返序列化文件,获取存储的文件对象
        ObjectInputStream getSetFiles = new ObjectInputStream(new FileInputStream("SocketPrj\\src\\FileDownLoad\\客户端配置文件.txt"));
        Object obj = getSetFiles.readObject();
        ArrayList<File> list = (ArrayList<File>) obj;
        getSetFiles.close();
        ArrayList<String> tempList = new ArrayList<>();
        System.out.println("服务器可供下载文件有:");
        for (File x : list) {
            tempList.add(x.getName());
            System.out.println(x.getName());
        }
        System.out.println("==================================");   //华丽的的分割线
        /*接收用户输入的想下载的文件,输入不存在的文件名
         * 就会一直处于输入状态
         * 输入正确跳出循环
         * 正确后向服务端发送下载请求*/
        while (true) {
            System.out.print("输入你想下载的文件名:");
            str = new Scanner(System.in).nextLine();
            if (tempList.contains(str)) {
                System.out.println("输入正确,等待下载...");
                break;
            } else {
                System.out.println("你输入的文件不存在!请重新输入:");
            }
        }
        /*向服务器发送下载请求*/
        OutputStream out = conSocket.getOutputStream();
        out.write(str.getBytes());
        conSocket.shutdownOutput();
        System.out.println("请求发送成功,等待下载...");
        conSocket.close();
        downFile();
    }

    /*接收服务端发来的文件*/
    public static void downFile() throws IOException {
        //因为第一个负责通信的socket使用了shutdownOutput,导致无法使用其继续发送数据
        //所以新开一个socket专门负责发文件
        fileSocket = new Socket(InetAddress.getLocalHost(), 8848);
        System.out.println("开始下载文件...");
        FileOutputStream writer = new FileOutputStream("SocketPrj\\ClientFiles\\" + str);
        InputStream in = fileSocket.getInputStream();
        byte[] bt = new byte[1024];
        int len;
        while ((len = in.read(bt)) != -1) {
            writer.write(bt, 0, len);
        }
        System.out.println("下载成功!");
    }
}

运行截图:

 

 

总结一句话就是:服务端,读出来-发出去 | 客户端,接收到,写进去

        我觉得其中有一个值得需注意的问题,就是需要对socket对象使用shutdownOutput方法,不使用这个方法,又会出现程序阻塞,有小伙伴知道阻塞原因吗?(嘿嘿嘿)。不过使用后就无法继续使用该socket对象发送数据,一时想不到解决办法,想的就是只有重新创建一个socket负责发送数据。但是感觉这样不妥,于是百度了下,好像这也算是一种解决办法,其他方法倒是没找到。不知道还有没有其他更好的方法。

        最后,不用知道为啥心血来潮,想写这篇博客,也算是第一个文章,哈哈哈哈,可能主要是向纪念下吧。当然,程序很简单,也有很多问题,还请大家不吝赐教。完整代码贴在上面了,有需要的小伙伴可以参考下哦,大家一起加油,冲冲冲

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值