快递驿站(C/S+MVC+集合+持久化+多线程)
- C/S:采用客户端与服务器模式的网络编程,客户端只是作为视图层与服务器间纽带,进行数据的传递,由视图层读取的数据交由客户端发送到服务器,服务器端调用dao操作数据增删改查,然后客户端接收服务器回写数据交给视图层展示。
- MVC:采用三层模式,view视图层只向用户展示和读取用户输入,c主要是逻辑控制,流程控制,接收请求,委托m处理数据并返回数据到v,,m主要是业务逻辑,对数据的操作。
- 数据持久化:将服务器接收的数据存入集合,将集合序列化到文件
- 多线程:在服务器端开启一个子线程用于服务器操作数据。
Socket实现一个客户端一个服务器端通信,进行快递信息的传递,服务器将接收快递持久化存入硬盘保存
客户端:1.将控制台快递信息传递给服务器
2.接收服务器回写信息,交给视图层展示
3.客户端作为视图层和服务器之间纽带,只负责数据的传递工作,快递增删改查在服务器完成。
视图层:1.(将控制台作为视图层)控制台接收用户输入,交给客户端;
2.接收客户端返回结果,显示到控制台。
3.视图层只与客户端通信
服务器:1.接收客户端信息,将信息交给DAO处理
2.DAO处理信息后(数据增删改查)将结果返回给服务器
3.服务器将结果发送给客户端
DAO: 1.进行数据的存储,各种增删改查方法
2.确保单号、取件码、存放位置 不重复
3.使用接口-实现类方式
实体类: 1.快递信息的封装 快递单号、公司名称、取件码、存放位置
信息处理工具类:
1.规定的命令(即规定数据增删改查)
2.收发信息的方法(客户端与服务器端)
提取一个IO操作工具类,负责将集合信息保存到磁盘(使用序列化、高速字符流、Properties都可)
首先写流程控制Main,循环主菜单和子菜单,并编写对应的视图层
然后开始编写子菜单中各个功能的实现
addExpress()添加快递的实现:
0.客户端先向服务器确认快递柜是否存满
1.从视图层(控制台)获取数据到客户端
2.客户端通过socket向服务器发送执行命令并将数据发给服务器
3.服务器端监听到客户端连接,进入子线程,根据客户端命令执行添加操作,
在添加方法中先接收客户端传来的数据,然后委托dao去添加数据,并将操作结果返回客户端。
4.客户端接收服务器回写数据,交给视图层展示
isFull():判满实现:
1.在主流程中调用客户端isFull方法判满
2.客户端isFull中向服务器发送判满命令,并等待接收服务器回写的布尔数据返回给主流程。
3.服务器端接收客户端连接,进入子线程,根据客户端命令执行判满操作(获取集合元素个数与
MAX比较),并将结果回写给客户端。
removeExpress():删除快递的实现:
1.在主流程中接收视图层输入的单号,然后调用客户端removeExpress删除快递
2.客户端removeExpress先向服务器发送删除命令并传递参数单号,然后等待服务器回写
3.服务器接收客户端连接,进入子线程,根据客户端命令执行删除操作,先接收客户端
传递来的单号,调用dao根据单号实现数据删除
4.服务器回写数据给客户端,客户端将数据返回到主流程中,在主流程中将结果传到视图层展示。
修改、查询、取件操作整体上和上面流程差不多,
1.都是从主流程中获取视图层输入的数据,
2.然后让客户端将要操作的指令和参数传递到服务器并等待回写
3.服务器接收到客户端连接,执行相应的指令并回写数据给客户端
4.客户端再将回写数据返给主流程,主流程将数据传给视图层展示。
bean包的Express实体类
package com.kdz.bean;
import java.io.Serializable;
/**
* 快递实体类
*/
public class Express implements Serializable {
private static final long serialVersionUID = 7859017056410332717L; //实现序列化接口
private String number;//快递单号
private String company;//快递公司
private int code;//取件码
private int target;//快递位置(0-100),显示时按第几排第几列显示,10排10列
public Express(String number, String company) {
this.number = number;
this.company = company;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public int getTarget() {
return target;
}
public void setTarget(int target) {
this.target = target;
}
@Override
public String toString() {
return "Express{" +
"number='" + number + '\'' +
", company='" + company + '\'' +
", code='" + showTarget()+'}';
}
/**
* 展示快递位置(第几排第几列),target存的时候是int,展示的时候是string
* @return
*/
public String showTarget() {
return target/10+"排"+target%10+"列";
}
}
MsgUtil信息处理工具类
客户端与服务器间的通信工具
package com.kdz.utils;
import com.kdz.bean.Express;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
/**C/S通信工具类
* 信息处理工具类:(只负责Client与Server间的信息处理)
* 1.规定的命令(即规定数据增删改查)
* 2.提取客户端与服务器收发信息的方法(即提取IO流操作)
*
* 客户端服务端都是靠Socket对象进行通信的,都要使用socket中的网络进行传输数据,
* 所以C/S通信封装的方法参数都少不了socket对象(借助流)对数据的发送和接收。
*/
public class MsgUtil {
//定义公开的常量命令(增删改查命令),通过命令,让服务器执行对应的方法
//服务器需要做的事情
public static final String CMD_ADD="ADD"; //添加命令
public static final String CMD_REMOVE="REMOVE"; //删除命令
public static final String CMD_MODIFY="MODIFY";//修改命令
public static final String CMD_QUERY="QUERY";//查询命令
public static final String CMD_SIZE="SIZE"; //查询快递柜中快递量
public static final String CMD_PICK="PICK"; //取快递
public static final String CMD_CHECKE_FULL = "FULL";//快递柜判空
public static final String CMD_FIND_BY_NUMBER ="FIND_BY_NUMBER";//通过单号判断快递存在否
/**
* 1.输出快递对象,客户端和服务器端都会用到
* 客户端发送express,服务器端接收
* 服务器端发送express,客户端接收
* @param socket
* @param express
*/
public static void sendExpress(Socket socket, Express express) throws IOException {
//将socket中网络输出流转换成(装饰)序列化输出流
//序列化流不仅可以文件上的对象传输,还可以网络上的对象传输,下面就是网络中传输对象
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(express);//输出对象
}
/**
* 2.接收快递对象,客户端和服务器都会用到
* @param socket
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Express receiveExpress(Socket socket) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
return (Express) ois.readObject();//读取对象并返回
}
/**
* 发送命令--命令是String类型
* @param socket
* @param cmd
*/
public static void sendStr(Socket socket,String cmd) throws IOException {
//推荐使用数据流(只要传的不是对象),序列化流有坑,先将socket网络流转成数据流
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(cmd);//输出字符串
}
/**
* 接收命令--命令是String类型
* @param socket
* @return
* @throws IOException
*/
public static String receiveCmd(Socket socket) throws IOException {
//将socket网络流转成数据流
DataInputStream dis = new DataInputStream(socket.getInputStream());
return dis.readUTF(); //读取字符串并返回
}
/**
* 发送Boolean类型数据(客户端与服务器间)
* @param socket
* @param flag
* @throws IOException
*/
public static void sendBoolean(Socket socket,Boolean flag) throws IOException {
//只要不是传对象,可使用数据流,将socket中网络流转成数据流
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeBoolean(flag);//输出布尔数据
}
/**
* 接收Boolean类型数据(客户端与服务器间)
* @param socket
* @throws IOException
*/
public static boolean receiveBoolean(Socket socket) throws IOException {
//将socket网络输入流转成数据流
DataInputStream dis = new DataInputStream(socket.getInputStream());
return dis.readBoolean();//读取布尔类型
}
/**
* 发送Int类型数据(客户端与服务器间)
* @param socket
* @param num
* @throws IOException
*/
public static void sendInt(Socket socket,int num) throws IOException {
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(num);//输出Int数据
}
/**
* 接收int类型数据(客户端与服务器间)
* @param socket
* @return
* @throws IOException
*/
public static int receiveInt(Socket socket) throws IOException {
DataInputStream dis = new DataInputStream(socket.getInputStream());
return dis.readInt(); //将读取int数据并返回
}
/**
* 接收list集合(c/s之间通信)
* 数据是对象,使用序列化流,不使用数据流
* @param socket
* @return
*/
public static ArrayList<Express> receiveList(Socket socket) throws IOException, ClassNotFoundException {
//socket中网络流转成序列化流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ArrayList<Express> list = (ArrayList<Express>) ois.readObject();//读取数据返回list
return list;
}
/**
* 发送list类型数据
* @param socket
* @param list
* @return
*/
public static void sendList(Socket socket,ArrayList<Express>list) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(list);//发送数据,若C发送则S接收,反之若S发送则C接收
}
}
IOUtil数据持久化工具类
package com.kdz.utils;
import com.kdz.bean.Express;
import java.io.*;
import java.util.ArrayList;
/**
* 数据持久化工具类
* 1.读取文件-->将数据转成集合
* 2.将集合-->写入文件
* 传递的是对象,使用序列化流最方便,对象的序列化操作。
*/
public class IOUtil {
private static File file=new File("快递信息.txt");//相对路径
/**
* 将数据(集合)写入文件
* 使用序列化流,写到文件的是对象的二进制数据,在文本中是乱码
* @param list
*/
public static void writeToFile(ArrayList<Express>list) {
//将文件流转成序列化流,使用JDK9的异常处理,自动释放资源
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)){
oos.writeObject(list);//将整个集合序列化到文件中
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从文件读取数据,使用反序列化流,将二进制读成集合对象
* 要预防文件不存在时的异常,保证可以拿到一个集合(即使是空的)
* @return
*/
public static ArrayList<Express> readFromFile(){
if (!file.exists()||file.length()==0) return new ArrayList<>();
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
return (ArrayList<Express>) ois.readObject();//读成一个集合并返回
} catch (IOException |ClassNotFoundException e) {
e.printStackTrace();
}
return new ArrayList<>();
}
}
Views视图层
package com.kdz.view;
import com.kdz.bean.Express;
import java.util.ArrayList;
import java.util.Scanner;
public class Views {
private Scanner sc=new Scanner(System.in);
/**
* 1.欢迎
*/
public void welcome(){
System.out.println("欢迎使用快递驿站");
}
/**
* 3.输入功能选项
* @param max
* @return
*/
public int inputKey(int max){
System.out.println("请输入选择:");
String s = sc.nextLine();
int num=-1;
try {
num = Integer.parseInt(s); //转换可能出现异常
}catch (Exception e){
}
if (num<0||num>max){ //输入不合理
System.out.println("输入错误,请重新输入");
num=inputKey(max); //输入选项不符合则继续调用方法
}
return num;
}
/**
* 2.展示主菜单
*/
public void showMainMenu(){
System.out.println("请选择用户类型:");
System.out.println("1.快递员 2.用户 3.退出程序");
}
/**
* 4.展示管理员子菜单
*/
public void showAdminMenu() {
System.out.println("请选择操作:");
System.out.println("1.增加快递 2.删除快递 3.修改快递 4.查看所有 5.返回主菜单");
}
/**
* 5.展示用户子菜单
*/
public void showUserMenu() {
System.out.println("请选择操作:");
System.out.println("1.取件 2.返回上级菜单");
}
/**
* 6.添加快递信息操作
* @return
*/
public Express inputExpress() {
String number = inputNumber(); //输入快递单号
String company = inputCompany();//输入快递公司
return new Express(number,company);
}
public String inputNumber() {
return inputStr("请输入快递单号:");
}
private String inputCompany() {
return inputStr("请输入快递公司:");
}
/**
* 7.封装方法专门在控制台接收用户输入的字符串
* 输入提示,接收一个字符串
* @param msg
* @return
*/
private String inputStr(String msg){
System.out.println(msg);
return sc.nextLine();
}
/**
* 8.视图层显示 客户端向服务器添加快递后服务器的回写信息(传回的是快递)
* @param add
*/
public void printResultAdd(Express add) {
System.out.println(add==null?"添加失败":"添加成功,存储位置:"+add.showTarget()+",取件码:"+add.getCode());
}
/**
* 9.当服务器通过客户端告知主流程快递柜满了,视图层给出提示
*/
public void full() {
System.out.println("快递柜已满");
}
/**
* 10.视图层给出删除后提示
* @param remove
*/
public void printResultRemove(Boolean remove) {
System.out.println(remove?"删除成功":"删除失败");
}
/**
* 11.未查到单号
*/
public void printNone() {
System.out.println("无此单号信息");
}
/**
* 12.修改快递后的结果
*/
public void printResultModify(Express modify) {
System.out.println(modify==null?"修改失败":"修改成功:"+modify);
}
/**
* 13.显示所有快递信息
* @param list
*/
public void showList(ArrayList<Express> list) {
if(list.size()==0){
showEmpty();
}else {
showTitle();
for (Express express:list){
System.out.printf("%-9s",express.getNumber());
System.out.printf("%-9s",express.getCompany());
System.out.printf("%-9s",express.getCode());
System.out.printf("%-9s",express.getTarget());
}
System.out.println();
}
}
/**
* 标题:快递字段
*/
private void showTitle() {
System.out.printf("%-6s","快递单号");
System.out.printf("%-6s","公司名称");
System.out.printf("%-6s","取件码");
System.out.printf("%-6s","存储位置");
System.out.println();
}
/**
* 列表中没有快递
*/
private void showEmpty() {
System.out.println("暂无任何快递信息");
}
/**
* 输入取件码
* @return
*/
public int inputCode() {
System.out.println("请输入取件码:");
String s = sc.nextLine();
int num=-1;
try {
num = Integer.parseInt(s); //转换可能出现异常
}catch (Exception e){
}
if (num<100000||num>999999){ //输入不合理
System.out.println("输入错误,请重新输入");
num=inputCode(); //输入取件码不符合则继续调用方法
}
return num;
}
public void printResultPick(Express express) {
System.out.println(express==null?"取件失败":"取件成功,存储位置:"+express.getTarget());
}
}
主流程Main
package com.kdz;
import com.kdz.bean.Express;
import com.kdz.client.ExpressClient;
import com.kdz.view.Views;
import java.io.IOException;
import java.util.ArrayList;
//主流程控制,先给出各个功能方法,写好整个流程后再实现各个功能
public class Main {
static Views v=new Views(); //视图层对象
static ExpressClient client=new ExpressClient();//客户端对象
public static void main(String[] args) throws IOException, ClassNotFoundException {
v.welcome();
boolean flag=true;//循环开关
while(flag){
v.showMainMenu();//显示主菜单
int key=v.inputKey(3);//用户输入功能号
switch(key){ //执行对应的功能
case 1:
adminLoop(); //进入管理员菜单循环
break;
case 2:
userLoop();//进入用户菜单循环
break;
case 3:
flag=false; //关闭循环开关,退出程序
client.close();//客户端关闭socket
break;
}
}
}
/**
* 1.进入管理员子菜单循环
*/
private static void adminLoop() throws IOException, ClassNotFoundException {
boolean flag = true;//循环开关
while (flag) {
v.showAdminMenu();//显示管理员子菜单
int key = v.inputKey(5);//用户输入功能号
//System.out.println("key:"+key);
switch (key) { //执行对应的功能(case退出switch)
case 1:
addExpress();
break;
case 2:
removeExpress();
break;
case 3:
modifyExpress();
break;
case 4:
findAllExpresses();
break;
case 5:
flag=false; //关闭循环开关,退出子菜单
break;
}
}
}
private static void findAllExpresses() throws IOException, ClassNotFoundException {
ArrayList<Express>list=client.findAll();//客户端向服务器要数据并传递过来
v.showList(list);//视图层来展示数据
}
/**
* 修改快递信息
* 需要通过客户端将单号传递给服务器去查有没有这个单号
* 服务器将查询结果返回客户端再返回到主流程这里
*/
private static void modifyExpress() throws IOException, ClassNotFoundException {
String number = v.inputNumber();//在视图层输入单号
if (client.findByNumber(number)==-1){
v.printNone();//视图层给出提示
return;//结束该次修改操作
}
//单号存在
Express express = v.inputExpress();
Express modify=client.modifyExpress(new Express(number,null),express);//服务器返回修改后对象
v.printResultModify(modify);//在客户端显示是否修改成功
}
/**
* 删除快递信息
* 也是通过客户端将待删除信息传递给服务器去删除
*/
private static void removeExpress() throws IOException {
String number=v.inputNumber(); //视图层输入待删除快递单号
Boolean remove=client.removeExpress(number);//客户端将单号传递给服务器删除并接收回写
v.printResultRemove(remove);
}
/**
* addExpress()添加快递的实现:
* 0.先去服务器查看快递柜是否满了,不满才能添加
* 1.从视图层(控制台)获取数据到客户端
* 获取快递单号,公司名称。(取件号和存储位置由程序负责)
* 2.客户端通过客户端对象将数据发给服务器
* 3.接收服务器回写数据,交给视图层展示
*/
private static void addExpress() throws IOException, ClassNotFoundException {
//添加前向服务器确认是否满了
if (client.isFull()){ //快递柜满了
v.full(); //在视图层给出提示
return;
}
Express express=v.inputExpress();//视图层来添加快递信息
Express add=client.addExpress(express);//客户端对象将快递发送给服务器
v.printResultAdd(add); //接收服务器回写数据(回写的是快递对象),交给视图层展示
}
/**
* 2.进入用户子菜单循环
*/
private static void userLoop() throws IOException, ClassNotFoundException {
boolean flag=true;//循环开关
while(flag){
v.showUserMenu(); //展示用户子菜单
int key = v.inputKey(2);//用户输入功能号
switch (key){ //switch执行相应功能
case 1:
pickExpress();
break;
case 2:
flag=false;//关闭循环开关,退出子菜单
break;
}
}
}
/**
* 用户取快递
*/
private static void pickExpress() throws IOException, ClassNotFoundException {
int code=v.inputCode();//在视图层输入取件码
Express express=client.pickExpress(code);//客户端命令服务器取出快递并返回
v.printResultPick(express);//视图层显示取件结果
}
}
客户端
package com.kdz.client;
import com.kdz.bean.Express;
import com.kdz.utils.MsgUtil;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
/**
* 快递的客户端
* 作为视图层(控制台)和服务器的纽带,只负责数据的传递
* 客户端与服务器端通信使用MsgUtil工具类即信息处理工具类负责两端通信
*/
public class ExpressClient {
private Socket socket;
/**
* 搭建客户端对象,占用端口号12345,服务器端是自身ip
* 当创建Socket对象时,就已经启动了客户端
*/
public ExpressClient(){
try {
socket = new Socket("127.0.0.1", 12345);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 关闭流 客户端socket
*/
public void close(){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 客户端咨询服务器快递柜是否满了
* @return
*/
public boolean isFull() throws IOException {
//调用C/S间信息处理工具类,告诉服务器执行CMD_CHECKE_FULL命令表示的方法
MsgUtil.sendStr(socket,MsgUtil.CMD_CHECKE_FULL);
return MsgUtil.receiveBoolean(socket);//客户端接收服务器回写信息
}
/**客户端添加操作
* 客户端并不直接添加,而是向服务器发送指令,让服务器调用DAO添加快递信息。
* 1.发命令
* 2.发需要的参数
* 3.收结果
* @param express
* @return
*/
public Express addExpress(Express express)throws IOException,ClassNotFoundException {
//客户端向服务器发送命令,使用信息处理工具类
MsgUtil.sendStr(socket,MsgUtil.CMD_ADD); //告诉服务器要添加操作
//向服务器传递要添加的快递
MsgUtil.sendExpress(socket,express);
//服务器添加完成,客户端接收回写数据(是对象)
Express add = MsgUtil.receiveExpress(socket);
return add;
}
/**
* 客户端删除操作
* 向服务器发送命令和参数,等待服务器接收并执行命令后回写数据,客户端再接收
* @param number
* @return
* @throws IOException
*/
public Boolean removeExpress(String number) throws IOException {
MsgUtil.sendStr(socket,MsgUtil.CMD_REMOVE); //向服务器发送命令
MsgUtil.sendStr(socket,number);//向服务器发送参数
boolean remove = MsgUtil.receiveBoolean(socket);//接收服务器回写数据
return remove;
}
/**
* 客户端根据单号查找快递是否存在
* 向服务器发送查找命令并传递参数number,并等待服务器端响应数据回来
* @param number
* @return
*/
public int findByNumber(String number) throws IOException {
MsgUtil.sendStr(socket,MsgUtil.CMD_FIND_BY_NUMBER);//向服务器发送查找命令
MsgUtil.sendStr(socket,number);//向服务器发送需要的数据
int num=MsgUtil.receiveInt(socket);//客户端等待接收服务器响应的数据,不存在则返回-1
return num;
}
/**修改快递信息
* 发送命令
* 传递数据(对象)
* 接收服务器响应数据
* @param oldExpress
* @param newExpress
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Express modifyExpress(Express oldExpress, Express newExpress) throws IOException, ClassNotFoundException {
MsgUtil.sendStr(socket,MsgUtil.CMD_MODIFY);//客户端向服务器发送修改命令
MsgUtil.sendExpress(socket,oldExpress);//向服务器传递旧对象
MsgUtil.sendExpress(socket,newExpress);//向服务器传递新对象
return MsgUtil.receiveExpress(socket);//等待接收服务器响应的数据(修改后的快递)
}
public ArrayList<Express> findAll() throws IOException, ClassNotFoundException {
MsgUtil.sendStr(socket,MsgUtil.CMD_QUERY);//客户端向服务器发送查询命令去查询
ArrayList<Express>list=MsgUtil.receiveList(socket);
//客户端等待接收服务器传回的查询信息,接收后返回
return list;
}
/**
* 取快递(根据取件码删除快递)
* 向服务器发送命令去执行取快递操作
* 向服务器发送需要的数据socket和code
* 然后等待接收服务器返回的数据
* @param code
* @return
*/
public Express pickExpress(int code) throws IOException, ClassNotFoundException {
MsgUtil.sendStr(socket,MsgUtil.CMD_PICK);//发送命令
MsgUtil.sendInt(socket,code);//发送数据
Express express = MsgUtil.receiveExpress(socket);//接收数据(取出的快递)
return express;
}
}
服务器端
package com.kdz.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端
* 使用客户端socket连接进行通信
* 使用多线程技术,一个服务器接收多个客户端
*/
public class ExpressServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12345);//搭建服务器并启动
System.out.println("服务器启动成功");
while(true){ //时刻监听客户端连接
Socket accept = ss.accept(); //获取客户端连接(即socket)
new Thread(new ExpressRunnable(accept)).start();//启动线程
}
}
}
服务器端子线程任务
package com.kdz.server;
import com.kdz.bean.Express;
import com.kdz.dao.ExpressDao;
import com.kdz.dao.impI.ExpressDaoImpI;
import com.kdz.utils.MsgUtil;
import java.io.IOException;
import java.net.Socket;
/**
* 服务器端线程任务,实现快递增删改查,实际上是调用Dao进行具体实现
*/
public class ExpressRunnable implements Runnable {
private Socket socket; //声明
private ExpressDao dao=new ExpressDaoImpI();
/**
* 构造器,在服务器端获取客户端连接socket,从而可获取客户端传递来的信息
* @param accept
*/
public ExpressRunnable(Socket accept) {
this.socket = accept;
}
@Override
public void run() {//线程任务
while(true){
try {
//服务器接收客户端传递来的命令
String cmd = MsgUtil.receiveCmd(socket);
if (cmd==null) break;
switch (cmd){
case MsgUtil.CMD_ADD: //添加命令
System.out.println("服务器端开始执行添加操作");
addExpress();
break;
case MsgUtil.CMD_CHECKE_FULL: //判满命令
checkFull();
break;
case MsgUtil.CMD_REMOVE://删除命令
System.out.println("服务器端开始执行删除命令");
removeExpress();
break;
case MsgUtil.CMD_FIND_BY_NUMBER:
System.out.println("服务器开始通过单号查找快递是否存在");
findNumber();
break;
case MsgUtil.CMD_MODIFY:
System.out.println("服务器开始修改快递信息");
modifyExpress();
break;
case MsgUtil.CMD_QUERY:
System.out.println("服务器开始查询所有快递信息");
findAll();
break;
case MsgUtil.CMD_PICK:
System.out.println("服务器开始取快递");
pickExpress();
break;
default:
System.out.println("无此指令");
break;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
try{
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
break;
}
}
}
/**
* 服务器取快递
* 即通过取件码删除数据
* 先接收客户端传递参数code
* 然后调用dao去根据code删除
* 然后响应客户端数据
*/
private void pickExpress() throws IOException {
int code = MsgUtil.receiveInt(socket);//接收取件码code
Express express = dao.removeByCode(code);//取出快递
MsgUtil.sendExpress(socket,express);//响应客户端,返回被删除数据
}
/**
* 服务器调用Dao查询所有快递信息
* 客户端ExpressClient中findAll方法中并没有向服务器传递数据,这里不需接收
* 但客户端在等着接收服务器返回的查询结果,这里要向客户端发送查询结果
*/
private void findAll() throws IOException {
MsgUtil.sendList(socket,dao.getAll());
}
/**服务器调用dao修改快递信息
* 客户端ExpressClient中modifyExpress方法向服务器发送修改命令,开始执行下面方法,
* 并传递两个快递对象,在下面进行接收
*/
private void modifyExpress() throws IOException, ClassNotFoundException {
//接收客户端传递的两个快递对象
Express oldExpress = MsgUtil.receiveExpress(socket);
Express newExpress = MsgUtil.receiveExpress(socket);
Express modify = dao.modify(oldExpress, newExpress);//调用dao去操作数据修改
MsgUtil.sendExpress(socket,modify);//响应客户端,回写数据,将修改后的快递返回
}
/**
* 服务器端调用DAO查询指定单号快递是否存在
* ExpressClient客户端中findByNumber方法向服务器发送命令和参数,在这里接收参数
*/
private void findNumber() throws IOException {
String number = MsgUtil.receiveCmd(socket);
int index = dao.findByNumber(number);
MsgUtil.sendInt(socket,index);//服务器将查询数据发送到客户端,客户端一直等待接收
}
/**
* 服务器调用DAO删除指定单号的数据
*/
private void removeExpress() throws IOException {
String number = MsgUtil.receiveCmd(socket);
//客户端发送删除命令,在这里接收命令,并读取传递来的number单号
boolean remove = dao.removeByNumber(number);//调用DAO删除指定数据,返回布尔
MsgUtil.sendBoolean(socket,remove);//服务器将布尔结果回写,客户端还在等着接收
}
/**
* 服务器端调用dao判断快递柜是否已满,将结果直接发送到客户端,客户端调用方法接收
*/
private void checkFull() throws IOException {
MsgUtil.sendBoolean(socket,dao.checkFull());
}
/**
* 服务器添加数据,调用DAO进行快递添加操作
*/
private void addExpress() throws IOException, ClassNotFoundException {
Express express = MsgUtil.receiveExpress(socket);//服务器接收客户端传递的快递
Express add= dao.add(express); //调用dao添加数据
MsgUtil.sendExpress(socket,add);//将添加结果发送给客户端
}
}
dao接口
package com.kdz.dao;
import com.kdz.bean.Express;
import java.util.ArrayList;
public interface ExpressDao {
Express add(Express express);//添加快递
boolean checkFull(); //判满
Express removeByCode(int code);//通过取件码删除快递
boolean removeByNumber(String number);//通过快递单号删除快递
int findByNumber(String number);
Express modify(Express oldExpress, Express newExpress);//修改快递信息
ArrayList<Express>getAll();//获取装快递的集合
}
dao实现类
package com.kdz.dao.impI;
import com.kdz.bean.Express;
import com.kdz.dao.ExpressDao;
import com.kdz.utils.IOUtil;
import java.util.ArrayList;
/**
* dao层操作数据增删改查的地方
*/
public class ExpressDaoImpI implements ExpressDao {
private ArrayList<Express> data;//若增删改多,可以使用linkedlist
private final int MAX=100; //快递柜最大存储数量
//使用构造代码块完成集合初始化,构造方法也可
//当创建Dao层对象时就会进行一个从磁盘读取数据的操作
{
data=IOUtil.readFromFile();
}
/**
* //定义快递存放位置,不重复
* @return
*/
private int initTarget(){
int target;
do {
target=(int)(Math.random()*MAX);//随机产生[0,100)之间的数
}while (findTarget(target)!=-1);//
return target; //返回没有被占用的坑位
}
/**
* //判断指定位置是否已有快递
* @param target
* @return
*/
private int findTarget(int target){
//遍历集合,判断已有快递的target是否有与当前target一致
for (int i = 0; i <data.size(); i++) {
if (data.get(i).getTarget()==target){ //这个坑位被占用了
return i;
}
}
return -1;//坑位没有被占用
}
/**
* 定义快递取件码,不重复
* @return
*/
private int initCode(){
int code;
do{
code= (int)(Math.random() * 900000 + 100000);
}while (findCode(code)!=-1);
return code;
}
/**
* 判断取件码是否已被使用
* @param code
* @return
*/
private int findCode(int code) {
//遍历集合,判断集合中已有快递的取件码与当前取件码是否有相同
for (int i = 0; i < data.size(); i++) {
if (data.get(i).getCode()==code){
return i;
}
}
return -1;
}
/**
* 判断快递柜是否已经满了
* @return
*/
@Override
public boolean checkFull() {
return data.size()>=MAX; //若集合元素>=MAX则满
}
//以上是添加快递前的准备操作
/**
*添加快递信息
* @param express
* @return
*/
@Override
public Express add(Express express) {
if(checkFull()){//满了
return null;
}
express.setCode(initCode());
express.setTarget(initTarget());
data.add(express);//向集合添加快递
IOUtil.writeToFile(data);//同步到文件中
return express;
}
/**
* 通过取件号删除快递
* @param code
* @return
*/
@Override
public Express removeByCode(int code) {
int index = findCode(code);//先根据code查找
if (index==-1) return null;//没找到
Express remove= data.remove(index);//找到,移除
IOUtil.writeToFile(data);//同步到文件中
return remove;
}
/**
* 通过单号删除快递
* @param number
* @return
*/
@Override
public boolean removeByNumber(String number) {
int index=findByNumber(number);//查找对应单号快递所在索引
if (index==-1) return false; //没找到,删除失败
data.remove(index);//找到,并删除
IOUtil.writeToFile(data);//同步到文件中
return true;
}
/**
* 通过单号查找快递,返回索引(位置),找不到返回-1
* @param number
* @return
*/
@Override
public int findByNumber(String number) {
for (int i = 0; i < data.size(); i++) {
if (number.equals(data.get(i).getNumber())){
return i;
}
}
return -1;
}
/**
* 修改快递信息
* 旧快递只保存了单号信息,通过单号信息找到旧快递位置,
* 新快递(单号和公司名称由控制台输入),取件码随机生成,然后将新快递放到就快点位置
* @param oldExpress
* @param newExpress
* @return
*/
@Override
public Express modify(Express oldExpress, Express newExpress) {
int index = findByNumber(oldExpress.getNumber());
if (index!=-1){//通过单号找到待修改快递位置index
newExpress.setTarget(data.get(index).getTarget());
newExpress.setCode(initCode());
data.set(index,newExpress);
IOUtil.writeToFile(data);//同步到文件中
return newExpress;
}
return null;
}
/**
* 获取所有快递信息
* @return
*/
@Override
public ArrayList<Express> getAll() {
return data;
}
}