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

326 阅读14分钟

一,快递管理训练任务

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 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)时也要遵循这样的规则

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

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