快递管理系统 v2.0

本文介绍了快递管理系统 v2.0 的更新,包括使用链表存储数据,加入服务器端和客户端的数据交互,以及文件存储功能。重点讲述了Socket通信、文件读写操作和序列化传递参数的实现。在解决客户端与服务器端数据不共通的问题上,通过在客户端实例化Express对象并发送给服务器端存储,最终解决了数据共享难题。
摘要由CSDN通过智能技术生成

在前面的文章里,我阐述过自己使用Java编写简易的快递管理系统的过程,这一次的更新算是一个跨度较大的更新,包含了全新的内容。对此有兴趣的伙伴可以继续往下阅读了解:

2.0版本新加入的特性:

  1. 使用链表存储数据;
  2. 加入服务器端(Server)和客户端(Client),并且加入了数据交互的操作;
  3. 加入文件存储部分,使用了文件流的功能
  4. 使用了“封装传参”的方法,将传输的数据都封装成参数再进行阐述。

接下来就对这些功能进行详细概述:

思路框架

本次设计的大体思路基本和前一次相同,都是面向对象进行编程。设计思路为:使用服务器和客户端进行交互,从客户端传入想要实现的命令,然后由服务器完成各项功能后再把数据返回。基于这一点,本次整个系统将分为5个类(class)进行编写,他们分别是:

快递类(Express)
作用:定义快递的数据结构和封装方式
管理类(Manage)
作用:对快递进行管理,实现各种操作
服务端(Server)
作用:接收客户端的数据,并执行客户端发送的请求
客户端(Client)
作用:使用快递系统,向服务端传输想要执行的任务
**指令端(Command)
作用:封装参数的传递方式==(难点)==

本文旨在阐述新特性的使用,因此,“快递类(Express)”的编写就不再赘述,大家可以参考上周的博文。

新功能概述

Part I. Socket 类的使用

  1. 首先创建客户端和服务端,请看如下代码:
// Server端
// 搭建服务器(参数为port)
ServerSocket server = new ServerSocket(5601);
System.out.println("服务器搭建成功,等待连接...");
// 等待连接
Socket s = server.accept();
// Client端
// 建立客户端,并连接到服务器(参数为:proxy,port)
Socket connect = new Socket("localhost", 5601);
  1. 接下来使用流来进行数据通信:
// 数据输出(传送)
OutputStream os = s.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("确认系统已启动!");

// 数据输入(接收)
InputStream is = connect.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String text = br.readLine();
System.out.println(text);

此时,可以从客户端控制台打印到“确认系统已启动!”的提示信息。

注意:在进行数据交互时,一定要避免双方都等待接收或双方都传送数据的情况发生,否则程序将出现死锁的状态,只能强制停止执行。

Part II. 文件读写操作

由于本次文件会存储对象数据,因此使用序列化进行读写操作,请看如下代码:

  1. 将数据写入文件的方法:
public void writeIn() throws IOException, ClassNotFoundException {
   
        // 把快递信息录入文件
        File f = new File("Express.txt");

        if (f.length()!=0){
   
            // 文件长度不为0时,把数据先读出,再追加写入
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Express.txt"));
            try{
   
                LinkedList<Express> saveBox = (LinkedList<Express>)ois.readObject();
                box.addAll(saveBox);
            }catch (EOFException ignored){
   }

            ois.close();

            // 序列化操作
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Express.txt"));
            out.writeObject(box);
            out.close();
        }else {
   
            // 文件长度为0时,直接写入
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Express.txt"));
            out.writeObject(box);
            out.close();
        }

    }
  1. 将数据读出的方法
public void loadAll() throws IOException, ClassNotFoundException {
   
        // 加载文件内的快递信息
        File f = new File("Express.txt");
        f.createNewFile();

        ObjectInputStream ois = null;
        try{
   
            // !文件没有数据时,就不进行加载
            ois = new ObjectInputStream(new FileInputStream("Express.txt"));
            LinkedList<Express> saveBox = (LinkedList<Express>)ois.readObject();
            // 添加快递数据到LinkedList内部
            box.addAll(saveBox);
        }catch (EOFException ignored){
   }

        if (ois!=null){
   
            ois.close();
        }
    }

Part III. 客户端与服务器端数据不共通(出错点)

编码时,第一次把管理快递的方法都放在Server端调用(即只在Server端实例化Manage),结果就出现了录入快递信息是在服务器端输入的情况,与实际使用相比稍有不合理,于是就想进行修改,让录入快递数据操作在Client端执行。只是万万没想到,这竟然是噩梦的开始——

第一次修改:在Client端也实例化一个Manage对象,调用快递录入的方法进行快递存储,结果在服务器端使用查看等方法时,发现链表里没有数据,即出现了下图所示的报错信息:
链表内部没有数据
第二次修改:在Manage内部把存储数据的链表改成静态(加上static修饰符),失败;

第三次修改:尝试把Server 端实例化的对象给到Client 端进行引用,即Client端使用Server端的实例化对象中的方法,仍然失败;

最终解决方案:在第三次修改后的基础上,Client端实例化“快递”对象(Express),等Client端添加好之后,再把对象数据发送到Server端,再让Server 端存储到链表中。最终成功。

问题原因:Client与Server运行时,二者使用的JVM(虚拟机)不同,因此数据不能直接进行共享,只能传输。

Extra. 序列化传递参数(难点)

在客户端与服务端进行数据传输时,发现3个功能下,传输的数据类型,数据量是不同的,并且在传输快递数据时,需要进行对象传送,需要实例化操作。因此干脆把需要传输的数据全部先进行对象化,然后再进行传输操作。

通过分析,本次客户端向服务端传输的数据可以分为两部分:一是操作指令(数字功能序号),二是参数传递(快递对象,取件码)。因此,再次新增Command类,直接对两个参数进行封装,再进行使用。

请看如下操作代码(同时注意看后面的源代码):

class Command

import java.io.Serializable;

/**
 * 请求命令对象
 *
 * type : 这个命令的类型(添加、删除、查询)
 * body : 这个命令的请求体
 *
 */
public class Command implements Serializable {
   
    // 使用时注意添加Serializable接口
    private int type;
    private byte[] body;

    public int getType() {
   
        return type;
    }

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

    public byte[] getBody() {
   
        return body;
    }

    public void setBody(byte[] body) {
   
        this.body = body;
    }
}

使用实例:Client端传入快递信息

// 传参对象进行字符数组序列化的准备
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
byte[] bytes = null;

// 在客户端进行快递录入操作
// 创建一个快递对象
Express ex = manage.createItem();

// 将快递对象进行序列化为byte数组
oos.writeObject(ex);
oos.flush()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值