任务卡_03-Java核心类库_第6节 网络编程

目录

一,快递管理训练任务

1,题目描述

2,源代码

2.1 bean.Express

2.2 dao.ExpressDao

2.3 main

2.4 view.View

3,总结

3.1 核心数据结构改动

3.2 ExpressDao

3.3 Main与ServerMain、ClientMain

3.4 说明

3.5 遇到的问题

3.6 思考

二,图书管理训练任务(选做)

1,题目描述


一,快递管理训练任务

1,题目描述

还记得之前的快递管理吗?

我们将数据存储在文件中,其实数据存储在客 户端中是很不安全的,今天我们来学习网络编程,客户端后续只用来收集用户 的操作,需要存储的数据都存储在服务器中。 

为了保证服务器能同时连接多个客户端,记得在服务器引入多线程技术。 接下来加油学习吧!

2,源代码

代码结构

2.1 bean.Express

package bean;

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

/**
 *
 */
public class Express implements Serializable {
    private String number;  // 快递单号
    private String company; // 公司
    private int code;       // 取件码
    public int posX, posY;  // 快递所在快递柜中的位置

    // 构造方法
    public Express(String number, String company, int code) {
        this.number = number;
        this.company = company;
        this.code = code;
    }

    public Express() {
    }

    // getter/setter

    public String getNumber() {
        return number;
    }

    public String getCompany() {
        return company;
    }

    public int getCode() {
        return code;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public void setCode(int code) {
        this.code = code;
    }

    // 重写toString 方法

    @Override
    public String toString() {
        return "Express{" +
                "number='" + number + '\'' +
                ", company='" + company + '\'' +
                ", code=" + code +
                '}';
    }

    // 重写equals方法

    /**
     * 只要快递单号相同就认为快递相同
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Express express = (Express) o;
        return Objects.equals(number, express.number);
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}

2.2 dao.ExpressDao

package dao;

import bean.Express;

import java.io.*;
import java.util.ArrayList;
import java.util.Random;

// 实现可序列化标记接口 使得dao对象支持序列化与反序列化
public class ExpressDao {
    private File file = new File("SerializedData.txt");
    private boolean[][] cabinet = new boolean[10][];            // 二维数组表示快递柜位置是否被占用 true已占用 false未占用
    private ArrayList<Express> expresses = new ArrayList<>();   // 存放所有的Express对象 便于遍历
    private Random random = new Random();                       // 用于生成随机数

    /**
     * 反序列化获得快递柜中存放的对象HashMap<Integer, Express> data
     */
    public void readFromFile() {
        try (FileInputStream fis = new FileInputStream(file)) {
            ObjectInputStream ois = new ObjectInputStream(fis);
            expresses = (ArrayList<Express>) ois.readObject();  // 反序列化读取对象
            ois.close();                                        // 关闭输入流
        } catch (IOException | ClassNotFoundException e) {
            expresses = new ArrayList<Express>();               // 打开文件异常时 将expresses初始为空
        }
    }

    /**
     * 序列化存储对象HashMap<Integer, Express> data
     * @throws IOException
     */
    public void writeToFile() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(expresses);                             // 序列化对象
        oos.close();
    }

    /**
     * 初始化数据结构:cabinet(表示快递柜是否被占用),expresses(存储)
     */
    public void init() {
        for(int i = 0; i < 10; i++){
            cabinet[i] = new boolean[10];
        }
        for(Express e : expresses) {
            cabinet[e.posX][e.posY] = true;         // 表示此位置已被占用
        }
    }

    /**
     *
     * @param e 新加入的快递对象
     * @return
     */
    public synchronized boolean add(Express e){
        int size = expresses.size();
        if(size >= 100){
            return false;
        }
        // 1,随机生成两个0-9的下标
        int x = -1, y = -1;
        while (true){
            x = random.nextInt(10);
            y = random.nextInt(10);
            if(cabinet[x][y] == false){
                break;                                  // 此位置未被占用
            }
        }
        // 2,判断取件码是否重复(最简单的 一个个对比)
        int code = randomCode();           // 获得没有重复的取件码
        e.setCode(code);
        e.posX = x;                                     // 快递柜存放快递的位置
        e.posY = y;
        size++;                                         // 快递数目加一
        cabinet[x][y] = true;                           // 此位置已被占用
        expresses.add(e);                              //
        return true;
    }

    /**
     * 遍历所有对象 生成独一无二的取件码
     * @return
     */
    private int randomCode(){
        while (true) {
            int code = random.nextInt(900000) + 100000; // 范围(000000-899999)+1000000
            Express e = findByCode(code);
            if(e == null) { // 说明取件码未重复
                return code;
            }
        }
    }

    /**
     * 快递员根据快递单号查询HashMap中存放的快递
     * @param number
     * @return
     */
    public Express findByNumber(String number){
        for(Express e : expresses) {
            if(e.getNumber().equals(number)) {
                return e;
            }
        }
        return null;
    }

    /**
     * 根据取件码查询快递
     * @param code 取件码
     * @return 查询到结果 查询失败返回null
     */
    public Express findByCode(int code){
        for(Express e : expresses) {
            if(e.getCode() == code) {
                return e;
            }
        }
        return null;
    }

    /**
     * 多余的操作 为了MVC更圆润
     * @param oldExpress
     * @param newExpress
     */
    public synchronized Boolean update(Express oldExpress, Express newExpress){
        delete(oldExpress);
        return add(newExpress);
    }

    /**
     * 删除特定的快递对象
     * @param e
     */
    public synchronized boolean delete(Express e){
        cabinet[e.posX][e.posY] = false;
        return expresses.remove(e);// 删除指定对象
    }

    /**
     * 获取所有的快递对象
     * @return
     */
    public synchronized ArrayList<Express> getAll() {
        return expresses;
    }
}

2.3 main

1)main.ClientMain

package main;

import bean.Express;
import view.View;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ClientMain {
    private Socket socket;
    private View v = new View();

    public static void main(String[] args) throws IOException {
        ClientMain client = new ClientMain();
        client.link();
    }

    /**
     * 创建套接字,与服务端进行连接;
     * 创建对象输入/输出流,与服务端进行数据交互;
     * @throws IOException
     */
    public void link() throws IOException {
        OutputStream os = null;
        InputStream is = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            socket = new Socket("127.0.0.1", 8888);
            v.connectSuccess();
            is = socket.getInputStream();
            os = socket.getOutputStream();
            oos = new ObjectOutputStream(os);// 由于服务器是先ois后oos 为了保证配对 这里需要顺序调换
            ois = new ObjectInputStream(is);
            o:while (true) {
                int num = v.menu();// 获得角色选择码
                oos.writeInt(num);
                oos.flush();
                switch (num) {
                    case 0:
                        break o;
                    case 1:
                        gClient(oos, ois);
                        break;
                    case 2:
                        uClient(oos, ois);
                        break;
                    default:
                        v.choiceError();
                        break;
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois != null) ois.close();
            if(oos != null) oos.close();
            if(socket != null) socket.close();
        }
    }

    /**
     * 客户端管理员模块
     * @param oos
     * @param ois
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void gClient(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
        while (true) {
            int num = v.gMenu();// 获得用户输入的功能码
            oos.writeInt(num);// 向服务器传送功能码 保证进入同样的功能模块
            oos.flush();
            switch (num) {
                case 0:// 退出
                    return;
                case 1:// 插入
                    insert(oos, ois);
                    break;
                case 2:// 修改
                    update(oos, ois);
                    break;
                case 3:// 删除
                    delete(oos, ois);
                    break;
                case 4:// 显示所有
                    printAll(oos, ois);
                    break;
                default:
                    v.choiceError();
                    break;
            }
        }
    }

    /**
     * 客户端用户模块
     * @param oos
     * @param ois
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void uClient(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
        while (true) {
            int num = v.uMenu();
            oos.writeInt(num);
            oos.flush();
            switch (num) {
                case 0:
                    return;
                case 1:
                    int code = v.getExpress();
                    oos.writeInt(code);
                    oos.flush();
                    Express e = (Express) ois.readObject();
                    if(e != null) {// 查询到有快递存在
                        v.printExpress(e);
                        if(ois.readBoolean()) v.success();
                        else v.fail();
                    } else {
                        v.printNull();// 取件码对应快递不存在
                    }
                    break;
                default:
                    v.choiceError();
                    break;
            }
        }
    }

    /**
     * 插入快递对象;
     * 利用view对象获取将要插入快递对象,并将其传送给服务端
     * @param oos
     * @param ois
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void insert(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
        Express e = v.insert();
        oos.writeObject(e);
        oos.flush();
        Express e1 = (Express) ois.readObject();// 返回对象为空 表示当前快递单号尚未被使用
        if(e1 == null) {
            if(ois.readBoolean()) {// 插入成功
                v.success();
            } else {
                v.fail();
            }
        } else {
            v.expressExist();
        }
    }

    /**
     * 删除快递对象
     * @param oos
     * @param ois
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void delete(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
        String id = v.findByNumber();
        oos.writeObject(id);
        oos.flush();
        Express e = (Express) ois.readObject();
        if(e != null) {
            int num = v.delete();// 再次向用户确认是否删除
            oos.writeInt(num);
            oos.flush();
            switch (num) {
                case 1:// 确认删除
                    if(ois.readBoolean()) v.success();
                    else v.fail();
                    break;
                default:// 取消删除或退出
                    break;
            }
        } else {
            v.printNull();
        }
    }

    /**
     * 更新快递对象
     * @param oos
     * @param ois
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void update(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
        String id = v.findByNumber();
        oos.writeObject(id);
        oos.flush();
        Express e = (Express) ois.readObject();
        if(e != null) {// 被更新的快递对象存在
            Express e1 = v.update();
            oos.writeObject(e1);

            oos.flush();
            if(ois.readBoolean()) v.success();
            else v.fail();
        } else {
            v.printNull();
        }
    }

    /**
     * 打印快递对象
     * @param oos
     * @param ois
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void printAll(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
//        ArrayList<Express> expresses = (ArrayList<Express>) ois.readObject();
//        v.printAll(expresses);
        Express[] es = (Express[]) ois.readObject();
        List<Express> expresses =  Arrays.asList(es);
        v.printAll(expresses);
    }
}

2)main.ServerMain

package main;

import bean.Express;
import dao.ExpressDao;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class ServerMain {
    private ServerSocket serverSocket;
    private ExpressDao dao = new ExpressDao();
    private int numOfClient = 0;
    // 服务器
    public static void main(String[] args) throws IOException {
        ServerMain server = new ServerMain();
        server.start();
    }

    /**
     * 启动服务器,并与客户端进行连接
     */
    public void start() {
        try {
            serverSocket = new ServerSocket(8888);
            System.out.println("服务器已启动");
            dao.readFromFile();// 从文件中读取数据
            dao.init();// 初始化数据结构
            System.out.println("数据初始化成功");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("第" + (++numOfClient) + "个客户端连接了");
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            receive(socket);// 准备连接 进入主功能模块
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(serverSocket != null){
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建对象输入/输出流,与客户端进行数据交互;
     * 进入主模块,选择角色;
     * @param socket
     * @throws IOException
     */
    public void receive(Socket socket) throws IOException {
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();
        ObjectInputStream ois = new ObjectInputStream(is);
        ObjectOutputStream oos = new ObjectOutputStream(os);
        try(is; os; ois; oos) {// 这种方式可以在try/catch执行结束后 自动关闭资源
            o: while (true) {
                switch (ois.readInt()) {
                    case 0:// 退出
                        dao.writeToFile();// 退出服务器端 将数据对象写回文件
                        break o;
                    case 1:
                        gClient(ois, oos);// 进入管理员操作功能模块
                        break ;
                    case 2:
                        uClient(ois, oos);// 进入用户操作功能模块
                        break ;
                    default: break ;
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 服务端——管理员模块,负责与客户端——管理员模块进行数据交互
     * @param ois
     * @param oos
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void gClient(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
        while (true) {
            switch (ois.readInt()) {
                case 0:// 退出
                    return;
                case 1:// 插入
                    insert(ois, oos);
                    break;
                case 2:// 修改
                    update(ois, oos);
                    break;
                case 3:// 删除
                    delete(ois, oos);
                    break;
                case 4:// 显示所有
                    printAll(ois, oos);
                    break;
            }
        }
    }

    /**
     * 服务端——用户模块,负责与客户端——用户模块进行数据交互
     * @param ois
     * @param oos
     * @throws IOException
     */
    public void uClient(ObjectInputStream ois, ObjectOutputStream oos) throws IOException {
        while (true) {
            switch (ois.readInt()) {
                case 0:
                    return;
                case 1:// 取件
                    Express e = dao.findByCode(ois.readInt());// 根据客户端传过来的取件码 查找快递对象
                    oos.writeObject(e);// 向客户端传送查找的对象
                    oos.flush();
                    if(e != null) {
                        oos.writeBoolean(dao.delete(e));
                        oos.flush();
                    }
                    break;
                default: break;
            }
        }
    }

    /**
     * 插入快递对象;
     * 接受客户端发来的新快递对象,用dao对象对数据进行操作,并将操作结果返还给客户端
     * @param ois
     * @param oos
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void insert(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
        Express e = (Express) ois.readObject();
        Express e1 = dao.findByNumber(e.getNumber());// 根据快递单号判断对应快递是否已存在
        oos.writeObject(e1);
        oos.flush();
        if(e1 == null) {
            oos.writeBoolean(dao.add(e));
            oos.flush();
        }
    }

    /**
     * 删除快递对象
     * @param ois
     * @param oos
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void delete(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
        String id = (String) ois.readObject();
        Express e = dao.findByNumber(id);
        oos.writeObject(e);
        oos.flush();
        if(e != null) {// 快递对象存在
            switch (ois.readInt()) {// 再次向用户确认是否删除
                case 1:
                    oos.writeBoolean(dao.delete(e));
                    oos.flush();
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 更新快递对象
     * @param ois
     * @param oos
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void update(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
        String id = (String) ois.readObject();
        Express e = dao.findByNumber(id);
        oos.writeObject(e);
        oos.flush();
        if(e != null) {
            Express e1 = (Express) ois.readObject();
            oos.writeBoolean(dao.update(e, e1));
            oos.flush();
        }
    }

    /**
     * 向客户端传送所有快递对象
     * @param ois
     * @param oos
     * @throws IOException
     */
    public void printAll(ObjectInputStream ois, ObjectOutputStream oos) throws IOException {
//        oos.writeObject(dao.getAll());
//        oos.flush();
        ArrayList<Express> list = dao.getAll();
        Express[] expresses = new Express[list.size()];
        list.toArray(expresses);
        oos.writeObject(expresses);
        oos.flush();
    }
}

2.4 view.View

package view;

import bean.Express;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;

/**
 * 视图层
 * 只负责展示视图 不包含其他任何逻辑
 */
public class View {
    public Scanner input = new Scanner(System.in);

    /**
     * 获得用户的角色选择输入,并进入相应的功能
     * @return 返回功能码 1:管理员 2:普通用户 0:退出
     */
    public int menu(){
        System.out.println("根据提示输入功能序号:");
        System.out.println("1,管理员");
        System.out.println("2,普通用户");
        System.out.println("0,退出");
        String s = input.nextLine();
        int funcNum = -1;
        try{
            funcNum = Integer.parseInt(s);
        }catch (NumberFormatException e){   // 格式异常 递归继续获取功能码
            return menu();
        }
        if(funcNum < 0 || funcNum > 2){     // 功能码不合法
            return menu();
        }
        return funcNum;
    }

    /*
    -----------------------------------------------------------------
     */

    /**
     * 获得管理员输入的功能码
     * @return 管理员输入的合法功能码 1:录入 2:修改 3:删除 4:查看所有 0:退出
     */
    public int gMenu(){
        System.out.println("根据提示输入功能序号:");
        System.out.println("1,快递录入");
        System.out.println("2,快递修改");
        System.out.println("3,快递删除");
        System.out.println("4,查看所有快递");
        System.out.println("0,退出");
        String s = input.nextLine();
        int funcNum = -1;
        try{
            funcNum = Integer.parseInt(s);
        }catch (NumberFormatException e){   // 格式异常 递归继续获取功能码
            return gMenu();
        }
        if(funcNum < 0 || funcNum > 4){     // 功能码不合法
            return gMenu();
        }
        return funcNum;
    }

    /**
     * 1快递员录入信息
     * @return 返回包含了快递单号和快递公司的快递对象
     */
    public Express insert(){
        System.out.println("请根据提示输入快递信息:");
        System.out.print("请输入快递单号:");
        String number = input.nextLine();
        System.out.print("请输入快递公司:");
        String company = input.nextLine();
        Express e = new Express();
        e.setNumber(number);
        e.setCompany(company);
        return e;
    }

    /**
     * 2修改快递信息
     */
    public Express update(){
        System.out.print("请输入新的快递单号:");
        String number = input.nextLine();
        System.out.print("请输入新的快递公司");
        String company = input.nextLine();
        Express e = new Express();
        e.setNumber(number);
        e.setCompany(company);
        return e;
    }

    /**
     * 3询问是否删除
     * @return 给出快递管理员的选择 1:删除 2:取消
     */
    public int delete(){
        System.out.println("确认是否删除:");
        System.out.println("1,确认删除");
        System.out.println("2,取消删除");
        System.out.println("0,退出");
        String s = input.nextLine();
        int num = -1;
        try {
            num = Integer.parseInt(s);
        }catch (NumberFormatException e){
            return delete();
        }
        if(num < 0 || num > 2){
            return delete();
        }
        return num;
    }

    /**
     * 4遍历显示所有快递信息
     * @param es
     */
    public void printAll(List<Express> es){
        int count = 0;
        for(Express e : es) {
            count++;
            printExpress(e);
        }
        if(count == 0){
            System.out.println("暂无快递信息");
        }
    }

    /**
     * 提示用户输入快递单号
     * @return
     */
    public String findByNumber(){
        System.out.println("请根据提示输入快递信息:");
        System.out.print("请输入需要操作的快递单号:");
        String number = input.nextLine();
        return number;
    }

    /**
     * 显示快递信息
     * @param e
     */
    public void printExpress(Express e){
        if(e == null){
            System.out.println("快递信息不存在");
            return;
        }
        System.out.println("快递信息如下:");
        System.out.println("位置:第" + (e.posX + 1) + "排," + (e.posY + 1) + "列; " +
                "快递公司:" + e.getCompany() + "; " + "快递单号:" + e.getNumber() + ";" +
                "取件码:" + e.getCode() + ";");
    }


    /*
    -----------------------------------------------------------------
     */

    /**
     * 获得用户输入的取件码(这里简化,只要取件码相同,就算取件成功)
     * @return 用户输入的合法功能码(6位)
     */
    public int uMenu(){
        System.out.println("根据提示输入功能序号:");
        System.out.println("0,退出");
        System.out.println("1,取出快递");
        String s = input.nextLine();
        int funcNum = -1;
        try{
            funcNum = Integer.parseInt(s);
        }catch (NumberFormatException e){   // 格式异常 递归继续获取功能码
            System.out.println("格式异常");
            return uMenu();
        }
        if(funcNum < 0 || funcNum > 1){     // 功能码不合法
            System.out.println("功能码不合法");
            return uMenu();
        }
        return funcNum;
    }

    /**
     * 用户取件
     * @return
     */
    public int getExpress() {
        System.out.println("根据提示进行取件:");
        System.out.print("请输入取件码:");
        String s = input.nextLine();
        int code = -1;
        try{
            code = Integer.parseInt(s);
        }catch (NumberFormatException e){   // 格式异常 递归继续获取功能码
            System.out.println("格式异常");
            return getExpress();
        }
        if(code < 100000 || code > 999999){     // 功能码不合法
            System.out.println("输入有误,请重试!");
            return getExpress();
        }
        return code;
    }

    public void expressExist(){
        System.out.println("此快递单号已存在,请勿重复存储");
    }
    public void printCode(Express e) {
        System.out.println("新快递的取件码为:" + e.getCode());
    }
    public void success(){
        System.out.println("操作成功!");
    }
    public void fail(){
        System.out.println("操作失败!");
    }
    public void choiceError() {
        System.out.println("输入选项有误,请重新输入!");
    }
    public void printNull(){
        System.out.println("快递不存在,请检查输入");
    }
    public void connectSuccess() {
        System.out.println("服务器连接成功");
    }
}

3,总结

与上一次的任务卡@&再见萤火虫&【任务卡_03-Java核心类库_第4节 IO】相比,有着较大的改动。

3.1 核心数据结构改动

将HashMap<Integer, Express> data删除,只保留Collection<Express> expresses;

  • 虽然可以根据HashMap快速的查找相应的Express对象,但是会导致快递对象的重复存储,也就是说在data中和expresses中存储了许多重复的快递对象,所以此次修改,只保留了expresses数据结构;

数据存储由main函数转移至dao对象中;

  • 将数据存储在main中,利用dao对象对数据进行操作需要传送大量的参数,使得函数看起来较为冗杂;
  • main函数中声明大量数据结构,使得主函数看起来较为复杂,破坏了整体的结构清晰感;

3.2 ExpressDao

1)将所有快递对象数据存放在dao的一个属性中(上一次IO任务卡,把他们放在了main方法中了);

2)取消根据取件码查找快递对象的HashMap数据结构,改成用ArrayList存储。避免对象重复存储占用大量空间;

3)插入、删除、更新、查询所有等方法使用synchronized进行修饰,保证线程安全;

3.3 Main与ServerMain、ClientMain

1)将Main拆解成ServerMain和ClientMain两个方法,一个负责服务端,一个负责客户端;

2)客户端和服务端需要保证大致相同的逻辑结构,即当客户端由状态1转变为状态2(方法功能,接受数据对象及类型等发生改变),服务端需要侦听这种状态转变,并进行同样的状态转移操作。反之同理;

3)客户端与服务端的数据交换是一去一回,严格配对!即便是在new输入输出流时也要保持一致!

4)服务器启动专门声明一个start方法完成,主要作用是:创建ServerSocket、反序列化读取对象信息、初始化数据结构、通过while循环实现多线程、线程run方法只用来调用receive方法(进入角色选择模块);

5)服务端的receive和客户端的link 是互相配对的。在这两个方法中创建ois(ObjectInoutStream)、oos(ObjectOutputStream),并将它们作为参数,进行数据交互;

3.4 说明

实现了多线程、集合、IO等技术;

较好的进行方法设计,使得项目整体结构清晰,冗余度较低;

基本完成题目要求功能:序列化反序列化、多线程、集合等;

3.5 遇到的问题

1)客户端执行到获取ObjectInputStream时卡住了,不再向下执行

咨询老师之后,发现问题。服务器与客户端在数据传输时,不但需要在交互阶段需要配对,声明赋值时也要配对,即输出对应输入、输入对应输出!

2)在dao中对数据进行操作后,快递对象集合确实发生变化了(在服务端打印出来看过)。但是在客户端进行printAll时,第一次是对的,后面不管怎么修改,再次printAll结果都不会改变

注释掉的,是原先有问题的代码。请教老师给出的解答是,流用完之后没有关闭,而且是作为参数进行传递,所以借助于流在客户端和服务器之间传递的集合对象expresses没有发生改变,依旧是第一次传输的数据,因而结果不发生改变。

但是有两点疑问:(以后再回头看看吧)

  1. flush的作用不就是清楚缓冲吗?为什么不起作用?
  2. 为什么把ArrayList对象转换为对象数组进行传输,就可以解决问题?是因为每次传输时,服务器都重新new了数组吗?

3.6 思考

1)try/catch与直接抛出异常相比有什么优缺点?

2)将流作为参数进行传递,不管对原数据进行什么操作,多次调用方法,获取流中传输的数据依旧是最开始传输的那一次?那flush的意以何在?

3)服务端、客户端进行数据交互时,不仅在传输数据需要一去一回,创建ois(ObjectInoutStream)、oos(ObjectOutputStream)时也要遵循这样的规则

二,图书管理训练任务(选做)

时间原因,有空再补o( ̄┰ ̄*)ゞ

1,题目描述

还记得之前的图书管理吗?我们将数据存储在文件中,其实数据存储在客 户端中是很不安全的,今天我们来学习网络编程,客户端后续只用来收集用户 的操作,需要存储的数据都存储在服务器中。 

为了保证服务器能同时连接多个客户端,记得在服务器引入多线程技术。 接下来加油学习吧! 

1. 管理员登陆 

2. 图书管理 

2.1 图书新增 

2.2 图书修改 

2.3 图书删除 

2.4 根据图书名称模糊查找图书 

2.5 查看所有图书 (三种排序) 

——价格从高到低排序 

——价格从低到高排序 

——新旧排序(出版日期排序)


章节汇总在这里(づ ̄3 ̄)づ╭❤~@&再见萤火虫&【03-Java核心类库】


对学习Java感兴趣的同学欢迎加入QQ学习交流群:1126298731

有问题欢迎提问,大家一起在学习Java的路上打怪升级!(o゜▽゜)o☆[BINGO!]

猜你喜欢

转载自blog.csdn.net/qq_41528502/article/details/109050980