1. 编程题

基于学生信息管理系统增加以下两个功能:
a.自定义学号异常类和年龄异常类,并在该成员变量不合理时产生异常对象并抛出。
b.当系统退出时将 List 集合中所有学生信息写入到文件中,当系统启动时读取文件中所 有学生信息到 List 集合中。

1.1 知识点

  1. 异常类的定义与使用
  2. IO流中写入对象的ObjectOutputStream类和ObjectInputStream的使用

    1.2 代码说明

  3. 异常类的定义格式说明

模块四作业 - 图1

  1. 在Student类中修改setId与setAge方法,当值不合理时抛出异常。
  2. 自定义异常类需序列化版本号
  3. 在ManageStudent类中增加无参构造方法默认从文件中读取学生信息
  4. 用户退出时写入学生信息到文件中

    1.3 code

    ```java package com.lagou.part4homework.manageStudent;

/**

  • @author 西风月
  • @date 2020/8/31
  • @description */ public class AgeException extends Exception { private static final long serialVersionUID = -5708679320894004827L;

    public AgeException() { }

    public AgeException(String message) {

    1. super(message);

    } } java package com.lagou.part4homework.manageStudent;

/**

  • @author 西风月
  • @date 2020/8/31
  • @description */ public class IDException extends Exception { private static final long serialVersionUID = -3636235950083905131L;

    public IDException() { }

    public IDException(String message) {

     super(message);
    

    } }

    ```java
    public void setId(int id) throws IDException {
         if(id > 0){
             this.id = id;
         }
         else{
             System.out.println("学号不合理哦!!!");
             throw new IDException("学号不合理哦!!!");
         }
     }
    public void setAge(int age) throws AgeException {
         if(age >= 4 && age <= 60){
             this.age = age;
         }
         else{
             System.out.println("年龄不合理哦!!!");
             throw new AgeException("年龄不合理哦!!!");
         }
     }
    

    ```java /**

  • 定义无参构造方法实现从f:/student.dat中读取文件 */ public ManageStudent() throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“F:/student.dat”)); Object object = ois.readObject(); this.studentList = (List) object; System.out.println(“Onload Student Info from [F:/student.dat] Suceess!”); }
    ```java
    public void saveToFile() throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f:/student.dat"));
    oos.writeObject(studentList);
    System.out.println("写入文件成功!");
    }
    

    1.4 截图

    ```java “C:\Program Files\Java\jdk-11.0.2\bin\java.exe” “-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=46830:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin” -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.manageStudent.MainTest Onload Student Info from [F:/student.dat] Suceess!
       学生信息管理系统

[1] 增加学生信息 [2] 删除学生信息 [3] 修改学生信息 [4] 查找学生信息

[5] 显示学生信息 [0] 退出学生系统

请选择具体的业务编号:

5

目前所有的学生信息是: Student [id=1001, name=aaa, age=24] Student [id=1002, name=bbb, age=24]

Student [id=8880, name=测试, age=30]

       学生信息管理系统

[1] 增加学生信息 [2] 删除学生信息 [3] 修改学生信息 [4] 查找学生信息

[5] 显示学生信息 [0] 退出学生系统

请选择具体的业务编号: 2 请输入要删除的学生学号: 1002 删除成功,被删除的学生信息是:Student [id=1002, name=bbb, age=24]

       学生信息管理系统

[1] 增加学生信息 [2] 删除学生信息 [3] 修改学生信息 [4] 查找学生信息

[5] 显示学生信息 [0] 退出学生系统

请选择具体的业务编号:

5

目前所有的学生信息是: Student [id=1001, name=aaa, age=24]

Student [id=8880, name=测试, age=30]

       学生信息管理系统

[1] 增加学生信息 [2] 删除学生信息 [3] 修改学生信息 [4] 查找学生信息

[5] 显示学生信息 [0] 退出学生系统

请选择具体的业务编号: 0 写入文件成功! 谢谢使用,再见! Process finished with exit code 0

<a name="AhWvi"></a>
# 2. 编程题 
  实现将指定目录中的所有内容删除,包含子目录中的内容都要全部删除。 
<a name="LONNR"></a>
## 2.1 知识点

1. File类的使用
1. 递归
<a name="Kj9Xc"></a>
## 2.2 代码说明

1. 如果文件类型是文件则删除
1. 文件类型是目录则删除目录中的文件
<a name="jvNEa"></a>
## 2.3 code
```java
package com.lagou.part4homework;

import java.io.File;

/**
 * @author 西风月
 * @date 2020/9/1
 * @description
 */

public class DeleteDirFile {
    private static void deleteDirFile(File[] files) {
        if(null == files) return;
        //foreach遍历目录元素并删除
        for(File file : files) {
            if(file.isFile()) {
                //如果文件类型是文件则删除
                file.delete();
                System.out.println("删除文件["+ file.getAbsolutePath() + "]成功!");
            } else if(file.isDirectory()) {
                //文件类型是目录则删除目录中的文件
                deleteDirFile(file.listFiles());
            }
        }
    }

    public static void main(String[] args) {
        File file = new File("F:/捣乱");
        deleteDirFile(file.listFiles());
    }
}

2.4 截图

"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=46964:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.DeleteDirFile
删除文件[F:\捣乱\猜猜我是谁\test.txt]成功!
删除文件[F:\捣乱\猜猜我是谁\你猜我猜不猜\888.txt]成功!
删除文件[F:\捣乱\猜猜我是谁\你猜我猜不猜\999.txt]成功!
删除文件[F:\捣乱\猜猜我是谁\你猜我猜不猜\死鬼\111.txt]成功!

Process finished with exit code 0

3. 编程题

使用线程池将一个目录中的所有内容拷贝到另外一个目录中,包含子目录中的内容。

3.1 知识点

  1. 线程池

模块四作业 - 图2
模块四作业 - 图3

  1. 使用Files工具类的copy方法复制文件
  2. Path类的使用

    3.2 代码说明

    多线程+递归方式深度拷贝目录。

    3.3 code

    ```java package com.lagou.part4homework;

import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Paths;

/**

  • @author 西风月
  • @date 2020/9/3
  • @description */ public class ThreadPoolCopy implements Runnable { //实现Runnable接口 String fromFile; String toFile; //构造函数传递文件路径信息 public ThreadPoolCopy(String fromFile, String toFile) {

     this.fromFile = fromFile;
     this.toFile = toFile;
    

    }

    @Override public void run() {

     try {
         //使用Files工具类的copy方法复制文件或目录
         System.out.println(Thread.currentThread().getName() +":"+"CP "+fromFile+"-->"+toFile);
         Files.copy(Paths.get(fromFile), Paths.get(toFile));
         //当目标文件已存在则会文件已存在异常,这是由于多线程调用拷贝函数时出现后进先执行的原因导致的。
     } catch (FileAlreadyExistsException e) {
         System.out.println(Thread.currentThread().getName() +":"+"File Already Exsited!");
     } catch (IOException e) {
         e.printStackTrace();
     }
    

    } }

```java
package com.lagou.part4homework;

import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 西风月
 * @date 2020/9/3
 * @description
 */
public class ThreadPoolCopyTest {

    static ExecutorService executorService = Executors.newCachedThreadPool();
    //递归+多线程的方式拷贝目录
    private static void fileCopy(String fileFrom, String fileTo) {
        if(fileFrom.equalsIgnoreCase(fileTo)) return;
        File f1 = new File(fileFrom);
        File f2 = new File(fileTo);
        if(!f1.exists()) return;    //如果源文件不存在则直接退出
        if(!f2.exists()) f2.mkdirs(); //如果目标目录不存在则新建目录
        File[] files = f1.listFiles();
        //遍历目录中的元素
        for(File tf : files) {
            if(tf.isFile()) {    //文件
                executorService.submit(new ThreadPoolCopy(fileFrom+"/"+tf.getName(), fileTo+"/"+tf.getName()));
            } else if(tf.isDirectory()) {  //子目录
                executorService.submit(new ThreadPoolCopy(fileFrom+"/"+tf.getName(), fileTo+"/"+tf.getName())); // 开辟新线程拷贝目录
                fileCopy(fileFrom+"/"+tf.getName(), fileTo+"/"+tf.getName());    //随后拷贝目录中的文件
            }
        }
    }

    public static void main(String[] args){
        String fileFrom = "F:/捣乱";
        String fileTo = "F:/测试";
        fileCopy(fileFrom, fileTo);
    }
}

3.4 截图

"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=4839:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.ThreadPoolCopyTest
pool-1-thread-5:CP F:/捣乱/猜猜我是谁/你猜我猜不猜-->F:/测试/猜猜我是谁/你猜我猜不猜
pool-1-thread-7:CP F:/捣乱/猜猜我是谁/你猜我猜不猜/死鬼-->F:/测试/猜猜我是谁/你猜我猜不猜/死鬼
pool-1-thread-6:CP F:/捣乱/猜猜我是谁/你猜我猜不猜/bbb.xml-->F:/测试/猜猜我是谁/你猜我猜不猜/bbb.xml
pool-1-thread-3:CP F:/捣乱/猜猜我是谁/1.jpg-->F:/测试/猜猜我是谁/1.jpg
pool-1-thread-2:CP F:/捣乱/猜猜我是谁-->F:/测试/猜猜我是谁
pool-1-thread-8:CP F:/捣乱/猜猜我是谁/你猜我猜不猜/死鬼/1.txt-->F:/测试/猜猜我是谁/你猜我猜不猜/死鬼/1.txt
pool-1-thread-1:CP F:/捣乱/new.doc-->F:/测试/new.doc
pool-1-thread-4:CP F:/捣乱/猜猜我是谁/C++.proc-->F:/测试/猜猜我是谁/C++.proc
pool-1-thread-5:File Already Exsited!
pool-1-thread-2:File Already Exsited!
pool-1-thread-7:File Already Exsited!

4. 编程题

使用基于 tcp 协议的编程模型实现将 UserMessage 类型对象由客户端发送给服务器;
服 务 器接 收到 对象 后判 断 用户 对象 信息 是否 为 “admin” 和 “123456”, 若 是则 将 UserMessage 对象中的类型改为”success”,否则将类型改为”fail”并回发给客户端,客户 端接收到服务器发来的对象后判断并给出登录成功或者失败的提示。
其中 UserMessage 类的特征有:类型(字符串类型) 和 用户对象(User 类型)。
其中 User 类的特征有:用户名、密码(字符串类型)。
如:
UserMessage tum = new UserMessage(“check”, new User(“admin”, “123456”));

4.1 知识点

  1. 对象输入输出流

模块四作业 - 图4
模块四作业 - 图5
模块四作业 - 图6

  1. 网络编程模型

模块四作业 - 图7
注意:
成员对象也需实现序列化的接口。

4.2 代码说明

4.3 code

**

package com.lagou.part4homework.homework4;

import java.io.Serializable;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class UserMessage implements Serializable {
    private static final long serialVersionUID = 5596033846618252122L;
    public String type;
    private User user;

    public UserMessage(String type, User user) {
        setType(type);
        setUser(user);
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
package com.lagou.part4homework.homework4;

import java.io.Serializable;
import java.util.Objects;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class User implements Serializable {
    private static final long serialVersionUID = -4388952945901863002L;
    private String userName;
    private String PassWord;

    public User(String userName, String passWord) {
        setUserName(userName);
        setPassWord(passWord);
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return PassWord;
    }

    public void setPassWord(String passWord) {
        PassWord = passWord;
    }

    //重写equals方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(userName, user.userName) &&
                Objects.equals(PassWord, user.PassWord);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userName, PassWord);
    }
}
package com.lagou.part4homework.homework4;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class ClientClass {
    public static void main(String[] args) {
        Socket s = null;
        Scanner sc = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            // (1) 创建Socket类型的对象并提供服务器IP地址和端口号
            s = new Socket("127.0.0.1", 8888);
            // (2) 使用输入输出流进行通信
            sc = new Scanner(System.in);
            System.out.println("=======欢迎登录=======");
            System.out.println("请输入用户名:");
            String userName = sc.next();
            System.out.println("请输入密码:");
            String password = sc.next();
            UserMessage um = new UserMessage("check", new User(userName, password));
            oos = new ObjectOutputStream(s.getOutputStream());
            oos.writeObject(um);
            ois = new ObjectInputStream(s.getInputStream());
            Object object = ois.readObject();
            UserMessage resp = (UserMessage) object;
            System.out.println("服务器返回:" + resp.type);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // (3) 关闭Socket
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            sc.close();
            try {
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.lagou.part4homework.homework4;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 * 使用基于 tcp 协议的编程模型实现将 UserMessage 类型对象由客户端发送给服务器;
 *   服 务 器接 收到 对象 后判 断 用户 对象 信息 是否 为 "admin" 和 "123456",
 *   若 是则 将 UserMessage 对象中的类型改为"success",否则将类型改为"fail"并回发给客户端,客户 端接收到服务器发来的对象后判断并给出登录成功或者失败的提示。
 *   其中 UserMessage 类的特征有:类型(字符串类型) 和 用户对象(User 类型)。
 *   其中 User 类的特征有:用户名、密码(字符串类型)。
 *       UserMessage tum = new UserMessage("check", new User("admin", "123456"));
 */
public class ServerClass {
    public static void main(String[] args) {
        ServerSocket sc = null;
        Socket s = null;
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        try {
            // (1) 创建ServerSocket类型的对象并提供端口号
            sc = new ServerSocket(8888);
            // (2) 等待客户端的连接请求,调用accept方法
            System.out.println("等待客户端连接....");
            s = sc.accept();
            System.out.println("客户端" + s.getInetAddress() + "连接成功!");
            // (3) 使用输入输出流进行通信
            //使用ObjectInputStream对象输入流接收客户端发来的消息
            ois = new ObjectInputStream(s.getInputStream());
            Object object = ois.readObject();
            UserMessage um = (UserMessage) object;
            if(um.getUser().equals(new User("admin", "123456"))) {
                um.type = "success";
                System.out.println("客户端用户名、密码校验成功!");
            } else {
                um.type = "fail";
                System.out.println("客户端用户名、密码校验失败!");
            }
            //返回给客户端校验信息
            oos = new ObjectOutputStream(s.getOutputStream());
            oos.writeObject(um);
            System.out.println("服务器发送回执消息成功!");
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            ///(4) 关闭Socket
            assert oos != null;
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                sc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4.4 截图

"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=5890:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework4.ClientClass
=======欢迎登录=======
请输入用户名:
admin
请输入密码:
123456
服务器返回:success

Process finished with exit code 0

"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=5883:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework4.ServerClass
等待客户端连接....
客户端/127.0.0.1连接成功!
客户端用户名、密码校验成功!
服务器发送回执消息成功!

Process finished with exit code 0
"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=5982:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework4.ClientClass
=======欢迎登录=======
请输入用户名:
admin
请输入密码:
888888
服务器返回:fail

Process finished with exit code 0

"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=5974:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework4.ServerClass
等待客户端连接....
客户端/127.0.0.1连接成功!
客户端用户名、密码校验失败!
服务器发送回执消息成功!

Process finished with exit code 0

5. 编程题

使用基于 tcp 协议的编程模型实现多人同时在线聊天和传输文件,要求每个客户端将发 送的聊天内容和文件发送到服务器,服务器接收到后转发给当前所有在线的客户端。

5.1 知识点

网络编程模型
输入/出流的使用
java.io.StreamCorruptedException: invalid type code: AC问题解决

5.2 代码说明

  1. 客户端开辟两个线程,分别是发送线程以及接收线程,保证通信的全双工
    2. 客户端发送线程与接收线程采用死循环保持会话
    3. 服务端开辟新线程处理客户端请求并转发
    4. 服务端采用List数据结构记录所有接入的Socket连接
    重写ObjectOutputSream的writeStreamHeader()方法
    向同一个Soclet中写入序列化对象,每次都会向文件中序列化一个header。在反序列化的时候每个 ObjectInputStream 对象只会读取一个header,那么当遇到第二个的时候就会报错,导致出现异常
    java.io.StreamCorruptedException: invalid type code: AC

5.3 code

package com.lagou.part4homework.homework5;

import java.io.Serializable;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class AMessage implements Serializable {
    private static final long serialVersionUID = -2083775012764186780L;
    public String type;    //消息类型:chat   file
    private String filename;  //文件名称
    private final byte[] buffer;   //消息内容

    public AMessage(String type, String filename, byte[] buffer) {
        this.type = type;
        this.filename = filename;
        this.buffer = buffer;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public byte[] getBuffer() {
        return buffer;
    }
}
package com.lagou.part4homework.homework5;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class ClientClass1 {
    public static void main(String[] args) {
        //(1) 与服务器建立连接
        Socket s = null;
        try {
            s = new Socket("127.0.0.1", 8888);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //(2) 开辟线程接收来自服务器的广播消息
        new ClientRecvThread(s).start();
        //(3) 开辟线程发送消息到服务器
        new ClientSendThread(s).start();
    }
}
package com.lagou.part4homework.homework5;

import java.io.IOException;
import java.net.Socket;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class ClientClass2 {
    public static void main(String[] args) {
        //(1) 与服务器建立连接
        Socket s = null;
        try {
            s = new Socket("127.0.0.1", 8888);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //(2) 开辟线程接收来自服务器的广播消息
        new ClientRecvThread(s).start();
        //(3) 开辟线程发送消息到服务器
        new ClientSendThread(s).start();
    }
}
package com.lagou.part4homework.homework5;

import java.io.EOFException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.nio.file.Files;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class ClientRecvThread extends Thread {
    Socket socket;

    public ClientRecvThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        ObjectInputStream ois = null;
        FileOutputStream fileOutputStream = null;

        try {
            ois = new ObjectInputStream(socket.getInputStream());
            while (true) {  //保持当前会话
                Object obj = ois.readObject();
                AMessage message = (AMessage) obj;
                if ("chat".equals(message.type)) {
                    System.out.println("接收到来自服务器的消息:[" + new String(message.getBuffer()) + "]!");
                } else if ("file".equals(message.type)) {
                    //截取文件名称
                    String path = message.getFilename();
                    String[] sp = path.split("/");
                    String filename = sp[sp.length - 1];
                    System.out.println("Received Filename=["+filename+"]");
                    //保存文件到本地
                    String fullName = "F:/tmp/" + filename;
                    fileOutputStream = new FileOutputStream(fullName);
                    fileOutputStream.write(message.getBuffer());
                    System.out.println("保存[" + filename + "]到本地成功");
                } else if ("bye".equals(message.type)) {
                    System.out.println("客户端接收线程退出!");
                    break;
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            //release
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                assert ois != null;
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.lagou.part4homework.homework5;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class ClientSendThread extends Thread {
    private Socket socket;
    private static int cnt = 0;

    public ClientSendThread(Socket socket) {
        this.socket = socket;
    }

    private void showMenu() {
        System.out.println("==========欢迎登录客户端!===========");
        System.out.println("1-发送消息(default)");
        System.out.println("2-发送文件");
        System.out.println("0-退出");
    }

    @Override
    public void run() {
        FileInputStream fis = null;
        Scanner sc = null;
        ObjectOutputStream oos = null;

        try {
            oos = new ObjectOutputStream(socket.getOutputStream());
            while(true) {
                showMenu();
                System.out.println("请选择指令:");
                sc = new Scanner(System.in);
                String opt = sc.next();
                if("2".equals(opt)) {
                    System.out.println("请输入您需发送文件的绝对路径!");
                    String path = sc.next();
                    fis = new FileInputStream(path);
                    int length = fis.available();
                    byte[] bytes = new byte[length];
                    if(fis.read(bytes) != -1) {
                        oos.writeObject(new AMessage("file", path, bytes));
                        System.out.println("发送文件 [" + path + "] 成功");
                    } else {
                        System.out.println("读取文件失败!");
                    }
                } else if("0".equals(opt)){
                    System.out.println("客户端发送线程退出!");
                    oos.writeObject(new AMessage("bye",null,null));
                    Thread.sleep(1000);
                    return;
                } else {
                    System.out.println("请输入您想发送的聊天内容:");
                    String str = sc.next();
                    oos.writeObject(new AMessage("chat", null, str.getBytes()));
                    System.out.println("发送消息成功!");
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            assert sc != null;
            sc.close();
            try {
                if (null != fis) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.lagou.part4homework.homework5;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

/**
 * @author 西风月
 * @date 2020/9/7
 * @description
 */
public class MyObjectOutputStream extends ObjectOutputStream {
    public MyObjectOutputStream(OutputStream out) throws IOException {
        super(out);
    }

    @Override
    protected void writeStreamHeader() throws IOException {
        return;
    }
}
package com.lagou.part4homework.homework5;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description
 */
public class ServerThread extends Thread {
    private Socket s;
    private static ArrayList<Socket> clients = new ArrayList<>();    //保存已有的Socket连接,类层级的变量

    public ServerThread(Socket s) {
        this.s = s;
        clients.add(s);
        System.out.println("从连接池添加客户端Socket成功!,当前的深度是:" + clients.size());
    }

    @Override
    public void run() {
        System.out.println("[" + Thread.currentThread().getName() + "]:客户端:[" + s.getInetAddress() + "]连接成功!");
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        ObjectOutputStream oosTmp = null;

        try {
            //1. 接收
            ois = new ObjectInputStream(s.getInputStream());
            oos = new ObjectOutputStream(s.getOutputStream());

            while (true) {  //保持会话
                Object object = ois.readObject();
                AMessage message = (AMessage) object;
                //2. 转发
                if ("chat".equals(message.type)) {
                    System.out.println("[" + Thread.currentThread().getName() + "]:" + "服务器收到消息["+ new String(message.getBuffer()) +"],开始转发!");
                } else if ("file".equals(message.type)) {
                    System.out.println("[" + Thread.currentThread().getName() + "]:" + "服务器收到文件["+ message.getFilename() +"],开始转发!");
                } else if ("bye".equals(message.type)) {    //当客户端发送线程输入退出指令后,相应的服务器线程退出
                    oos.writeObject(message);    //发送退出指令到客户端接收线程
                    System.out.println("[" + Thread.currentThread().getName() + "]:" + "客户端[" + s.getLocalAddress() + "]退出!");
                    break;  //退出线程
                }
                //分发消息或文件到其他客户端
                for (Socket socket : clients) {
                    if (s == socket) continue;
                    oosTmp = new MyObjectOutputStream(socket.getOutputStream());    //打开该输出流
                    oosTmp.writeObject(message);
                    System.out.println("[" + Thread.currentThread().getName() + "]:" + "转发到client[" + socket.getLocalAddress() + "]消息成功!");
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            //3. 关闭Socket并释放相关资源
            clients.remove(s);    //从连接池删除当前连接
            System.out.println("[" + Thread.currentThread().getName() + "]:" + "从连接池删除客户端Socket成功!,当前的深度是:" + clients.size());

            try {
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.lagou.part4homework.homework5;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 西风月
 * @date 2020/9/6
 * @description 服务器主处理模块:
 */
public class ServerClass {
    public static void main(String[] args) {
        ServerSocket ss = null;
        try {
            //1. 创建ServerSocket对象并指定端口
            ss = new ServerSocket(8888);
            //2. 等待客户端的请求,调用accept方法
            while (true) {
                System.out.println("等待客户端连接......");
                Socket accept = ss.accept();
                //3. 新启动线程处理接入的请求
                new ServerThread(accept).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 关闭Socket并释放相关资源
            try {
                if (ss != null) {
                    ss.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5.4 截图

"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=8790:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework5.ClientClass1
==========欢迎登录客户端!===========
1-发送消息(default)
2-发送文件
0-退出
请选择指令:
1
请输入您想发送的聊天内容:
在吗
发送消息成功!
==========欢迎登录客户端!===========
1-发送消息(default)
2-发送文件
0-退出
请选择指令:
接收到来自服务器的消息:[不是本人]!
1
请输入您想发送的聊天内容:
88
发送消息成功!
==========欢迎登录客户端!===========
1-发送消息(default)
2-发送文件
0-退出
请选择指令:
2
请输入您需发送文件的绝对路径!
F:/a.txt
发送文件 [F:/a.txt] 成功
==========欢迎登录客户端!===========
1-发送消息(default)
2-发送文件
0-退出
请选择指令:
0
客户端发送线程退出!
客户端接收线程退出!

Process finished with exit code 0
"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=8801:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework5.ClientClass2
==========欢迎登录客户端!===========
1-发送消息(default)
2-发送文件
0-退出
请选择指令:
接收到来自服务器的消息:[在吗]!
1
请输入您想发送的聊天内容:
不是本人
发送消息成功!
==========欢迎登录客户端!===========
1-发送消息(default)
2-发送文件
0-退出
请选择指令:
接收到来自服务器的消息:[88]!
Received Filename=[a.txt]
保存[a.txt]到本地成功

0
客户端发送线程退出!
客户端接收线程退出!

Process finished with exit code 0
"C:\Program Files\Java\jdk-11.0.2\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=8778:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\IdeaProjects\javase\out\production\javase com.lagou.part4homework.homework5.ServerClass
等待客户端连接......
从连接池添加客户端Socket成功!,当前的深度是:1
等待客户端连接......
[Thread-0]:客户端:[/127.0.0.1]连接成功!
从连接池添加客户端Socket成功!,当前的深度是:2
等待客户端连接......
[Thread-1]:客户端:[/127.0.0.1]连接成功!
[Thread-0]:服务器收到消息[在吗],开始转发!
[Thread-0]:转发到client[/127.0.0.1]消息成功!
[Thread-1]:服务器收到消息[不是本人],开始转发!
[Thread-1]:转发到client[/127.0.0.1]消息成功!
[Thread-0]:服务器收到消息[88],开始转发!
[Thread-0]:转发到client[/127.0.0.1]消息成功!
[Thread-0]:服务器收到文件[F:/a.txt],开始转发!
[Thread-0]:转发到client[/127.0.0.1]消息成功!
[Thread-0]:客户端[/127.0.0.1]退出!
[Thread-0]:从连接池删除客户端Socket成功!,当前的深度是:1
[Thread-1]:客户端[/127.0.0.1]退出!
[Thread-1]:从连接池删除客户端Socket成功!,当前的深度是:0