day30综合案例-学员管理系统【CS版】

    技术2023-09-10  112

    今日内容

    学员管理系统

    学习目标

    能够完成客户端添加功能 能够完成客户端修改功能 能够完成客户端删除功能 能够完成客户端获取功能 能够完成服务端功能

    一 项目演示

    1 打开项目

    目录结构如下图:

    2 运行项目

    运行服务器端:

    ​ 注:服务器使用端口:8888

    运行客户端:

    执行添加、修改、删除、查询:

    ​ 注:保存学生信息的文件user.txt存放到项目根目录

    二 项目说明

    1 所采用的知识点

    ​ 本系统采用了我们学过的以下几个核心知识点:

    ​ 1). IO流技术

    ​ 2). 网络编程技术

    ​ 3). 序列化

    ​ 4). 多线程

    2 业务交互模式图示

    注意:

    ​ 1.客户端向服务器端发送的操作(增删改查)的序号使用[]括起来了,不括起来也可以

    ​ 2.对于删 改和根据编号id查询一个人的信息,都是客户端向服务器端发送的是学员学号即id.

    ​ 【说明】

    ​ 1).客户端和服务器端采用TCP连接;

    ​ 2).数据保存在服务器端;

    ​ 3). 客户端增删改查发送数据格式说明:

    ​ a). 添加:"[1]数据",例如:"[1]张三,男,22",意思:没有id字段,由服务器端在写入数据前自动添加。

    ​ b). 修改一条数据:"[3]新数据"。例如:"[3]1,张三2,女,19",意思:将id=1的学员改为后面的新数据。

    ​ c). 查询所有数据:"[4]"。例如:"[4]",意思:后面不用带任何数据。

    ​ d). 删除一条数据:"[5]id"。例如:"[5]1",意思:删除id为1的记录。

    三 案例代码

    1.项目包结构

    2.实体类(Student类)

    package com.itheima.sh.domain; import java.io.Serializable; public class Student implements Serializable { //定义自动增加的编号 public static int idCount = 1; //构造代码块 //在每次创建对象时会自动执行,每执行一个就会让编号加一 { idCount++; } //成员变量 //编号 private int id; //姓名 private String name; //年龄 private int age; //居住地 private String address; //构造方法 public Student() { } public Student(int id, String name, int age, String address) { //idCount++; this.id = id; this.name = name; this.age = age; this.address = address; } //setget public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } //toString @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } }

    说明:

    ​ 1.因为学生对象要被序列化到硬盘文件中,所以Student类需要实现序列化接口Serializable

    3.客户端主界面搭建

    public class DemoCilent { public static void main(String[] args) throws Exception { //创建客户端对象 Socket s = new Socket("127.0.0.1",8888); //键盘录入 Scanner sc = new Scanner(System.in); //让用户选择12345 while(true){ //给用户显示数据 System.out.println("请选择你要完成的操作:"); System.out.println("1. 增加学生 2.删除学生 3.修改学生 4.查看所有学生 5.退出"); System.out.println("------------------------------------------------------"); int choose = sc.nextInt(); //判断用户选择的是什么操作 switch (choose){ case 1: //增加学生 break; case 2: //删除学生 break; case 3: //修改学生 break; case 4: //查看所有学生 break; case 5: //关闭资源 s.close(); //退出 return; } } } }

    说明:因为还没有服务器,所以想运行客户端需要去掉连接服务器的代码

    Socket s = new Socket("127.0.0.1",8888);

    4.添加功能

    4.1客户端

    //添加方法 private static void add(Socket s) throws IOException { //输入要添加的学生信息 Scanner sc = new Scanner(System.in); System.out.println("请输入要添加的学生姓名:"); String name = sc.next(); System.out.println("请输入要添加的学生年龄"); int age = sc.nextInt(); System.out.println("请输入要添加的学生居住地"); String address = sc.next(); //把学生信息发给服务器 //拼接字符串 String str = "[1]"+name+","+age+","+address; //获取输出流 OutputStream out = s.getOutputStream(); //把字符串转成字节数组发送数据 out.write(str.getBytes()); //-------------------------------------------- //接收服务器发回的数据 InputStream in = s.getInputStream(); //创建数组用于接受数据 byte[] bys = new byte[1024]; //读取 int len = in.read(bys); //打印信息 System.out.println(new String(bys,0,len)); }

    说明:学生id不用输入,在服务器端自动生成。

    4.2服务器端

    说明:

    ​ 1.对于服务器端我们需要考虑起初客户端连接服务器的时候,项目下是否含有存储学生对象的文件,如果没有需要创建文件,并将空的list集合放到文件中,否则会报找不到文件异常

    ​ 2.使用死循环让服务器一直运行,使用多线程为每个客户端开启一个线程

    public class DemoServer { public static void main(String[] args) throws Exception { //创建对象 ServerSocket ss = new ServerSocket(8888); //在开启服务器的时候就提前创建一个文件 //判断文件是否存在,如果已存在就不创建 如果不存在就创建 File f = new File("user.txt"); if (!f.exists()) { //在文件里面保存空集合 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt")); //创建集合 ArrayList<Student> list = new ArrayList<>(); //输出集合 oos.writeObject(list); } //循环 //连接多个客户端 while (true) { //连接客户端 //阻塞方法 Socket s = ss.accept(); //连接上客户端之后给客户端开启一个线程 new Thread(new Runnable() { @Override public void run() { //一个客户端的多次操作 while (true) { try { //处理客户端的操作 //接受客户端发来的数据 InputStream in = s.getInputStream(); byte[] bys = new byte[1024]; //读取 int len = in.read(bys); String str = new String(bys, 0, len); //获取用户的操作 char choose = str.charAt(1); //获取信息 String message = str.substring(3); //判断用户的操作 switch (choose) { case '1': //添加数据 add(message); break; case '2': //删除数据 break; case '3': //修改数据 break; case '4': //查询所有数据 break; } } catch (Exception e) { } } } //添加方法 private void add(String message) throws IOException, ClassNotFoundException { //老王,40,隔壁 //切割 String[] strs = message.split(","); //封装成一个学生对象 //Student.idCount 获取idCount的值赋值给学生的id Student stu = new Student(Student.idCount, strs[0], Integer.parseInt(strs[1]), strs[2]); //把对象保存到文件中 //对象输入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); //读取已有的内容 ArrayList<Student> list = (ArrayList<Student>) ois.readObject(); //对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt")); //把新增的对象添加到集合中 list.add(stu); //把集合输出到文件中 oos.writeObject(list); //给用户会送响应 OutputStream out = s.getOutputStream(); out.write("添加成功".getBytes()); } }).start(); } } }

    5.查看功能

    5.1客户端

    //查询所有方法 private static void findAll(Socket s) throws IOException, ClassNotFoundException { //用户没有任何信息需要输入 //直接给服务器发送请求 OutputStream out = s.getOutputStream(); out.write("[4]".getBytes()); //接受服务器发回来的信息 //收取对象 //反序列化流关联的是连接通道所以使用s.getInputStream()获取字节输入流 ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); //读取 ArrayList<Student> list = (ArrayList<Student>)ois.readObject(); //遍历集合打印数据 for (Student stu : list) { System.out.println(stu); } }

    说明:

    因为要从和服务器的连接通道中读取对象数据,所以这里的反序列化流关联的是连接通道所以使用s.getInputStream()获取字节输入流。

    ObjectInputStream ois = new ObjectInputStream(s.getInputStream());

    5.2服务器

    //查询所有 private void findAll() throws IOException, ClassNotFoundException { //读取文件中的集合 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); ArrayList<Student> list = (ArrayList<Student>) ois.readObject(); //把集合用对象的方式发给客户端 ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream()); oos.writeObject(list); }

    说明:

    ​ 1.从文件读取集合,所以使用new FileInputStream(“user.txt”)获取字节输入流

    ​ 2.将集合写到连接通道中,所以使用s.getOutputStream()获取字节输出流

    6.删除功能

    6.1客户端

    //删除方法 private static void remove(Socket s) throws IOException { //让用户输入删除的编号 Scanner sc = new Scanner(System.in); System.out.println("请输入你要删除的学生编号:"); int id = sc.nextInt(); //给服务器发送数据 String str = "[2]"+id; //发送 OutputStream out = s.getOutputStream(); out.write(str.getBytes()); //接受服务器发回的数据 InputStream in = s.getInputStream(); byte[] bys = new byte[1024]; int len = in.read(bys); //打印结果 System.out.println(new String(bys,0,len)); }

    6.2服务器端

    //删除方法 private void remove(String message) throws IOException, ClassNotFoundException { //读取文件中的所有数据 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); //读取 ArrayList<Student> list = (ArrayList<Student>) ois.readObject(); //把字符串转成整数 int id2 = Integer.parseInt(message); //获取输出流 OutputStream out = s.getOutputStream(); //判断集合中是否存在这个编号 for (int i = 0; i < list.size(); i++) { //获取集合中的学生 Student stu = list.get(i); //获取集合中的编号 int id = stu.getId(); //如果存在就删除并返回删除成功 if (id2 == id) { //删除对象 list.remove(i); //给客户端发送删除成功 out.write("删除成功".getBytes()); //要更新文件 //对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt")); oos.writeObject(list); //结束方法 return; } } //如果不存在就返回编号不存在 out.write("编号不存在".getBytes()); }

    7.修改功能

    7.1客户端

    //修改方法 private static void update(Socket s) throws IOException { //输入学生信息 Scanner sc = new Scanner(System.in); System.out.println("请输入要修改的学生编号"); int id = sc.nextInt(); System.out.println("请输入新的姓名:(如果输入0表示保留原值)"); String name = sc.next(); System.out.println("请输入新的年龄:(如果输入0表示保留原值)"); int age = sc.nextInt(); System.out.println("请输入新的居住地:(如果输入0表示保留原值)"); String address = sc.next(); //把信息拼接成字符串 String str = "[3]"+id+","+name+","+age+","+address; //发送 OutputStream out = s.getOutputStream(); out.write(str.getBytes()); //接受会送结果 InputStream in = s.getInputStream(); byte[] bys = new byte[1024]; int len = in.read(bys); //打印 System.out.println(new String(bys,0,len)); }

    7.2服务端

    //修改方法 private void update(String message) throws IOException, ClassNotFoundException { //拆分字符串 String[] arr = message.split(","); //字符串形式的id转成整数 int id2 = Integer.parseInt(arr[0]); //获取文件中的集合 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt")); ArrayList<Student> list = (ArrayList<Student>) ois.readObject(); //判断id是否存在 for (int i = 0; i < list.size(); i++) { //获取集合中的学生 Student stu = list.get(i); //获取学生的id int id = stu.getId(); //如果id存在 修改成功 if (id == id2) { //判断各个值是否是0 if (!arr[1].equals("0")) stu.setName(arr[1]); if (!arr[2].equals("0")) stu.setAge(Integer.parseInt(arr[2])); if (!arr[3].equals("0")) stu.setAddress(arr[3]); //把信息更新到文件中 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt")); oos.writeObject(list); //给用户反馈 OutputStream out = s.getOutputStream(); out.write("修改成功".getBytes()); //结束方法 return; } } //如果id不存在 修改失败 OutputStream out = s.getOutputStream(); out.write("修改失败".getBytes()); }
    Processed: 0.011, SQL: 9