day30综合案例-学员管理系统【C/S版】

今日内容

  • 学员管理系统

学习目标

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

一 项目演示

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());
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃 哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值