JAVA 技术方向支线任务-快递管理训练任务
1. 任务概述: 还记得之前的快递管理吗?我们将数据存储在文件中,其实数据存储在客 户端中是很不安全的,今天我们来学习网络编程,客户端后续只用来收集用户 的操作,需要存储的数据都存储在服务器中。 为了保证服务器能同时连接多个客户端,记得在服务器引入多线程技术。
2.代码结构
3.源代码
3.1. bean.Express
package bean;
import java.io.Serializable;
/**
快递类
*/
public class Express implements Serializable {
//快递单号
private String order;
//公司名称
private String compony;
//取件码、快递位置
private int code,target;
public Express(String order, String compony) {
this.order = order;
this.compony = compony;
}
public Express() {
}
private String showTarget() {
return target/10+"排"+target%10+"列";
}
@Override
public String toString() {
return "快递单号:"+order+"\t快递公司:"+compony+"\t取件码:"+code+"\t"+showTarget();
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
public String getCompony() {
return compony;
}
public void setCompony(String compony) {
this.compony = compony;
}
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;
}
}
3.2. dao.ExpressDao接口与ExpressDaoImpl实现类
package dao;
import bean.Express;
import java.io.IOException;
import java.util.ArrayList;
public interface ExpressDao {
/**
* 添加方法
* @param express
* @return
*/
Express add(Express express);
/**
* 删除快递
* @param order
* @return
*/
boolean delete(String order);
/**
* 修改快递
* @param oldExp
* @param newExp
* @return
*/
Express update(Express oldExp, Express newExp);
/**
* 查询所有快递信息
* @return
*/
ArrayList<Express> getAll();
/**
* 取快递
* @param code
* @return
*/
boolean getExpress(int code);
/**
* 数组大小
* @return
*/
int size();
/**
*根据快递号查询快递信息
* @param order
* @return
*/
Express findByOrder(String order);
/**
* 根据取件查询快递信息
* @param order
* @return
*/
Express findByCode(int order);
/**
* 从文件中读取快递信息(反序列化)
*/
void readFromFile();
/**
* 将信息写入文件中(序列化)
* @throws IOException
*/
void writeToFile() throws IOException;
}
package dao.impl;
import bean.Express;
import dao.ExpressDao;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
public class ExpressDaoImpl implements ExpressDao {
private File file = new File("Express.txt");
private ArrayList<Express> list;
private Random random = new Random();
//快递柜
private int max = 100;
/**
* 反序列化获得快递柜中存放的对象HashMap<Integer, Express> data
*/
public void readFromFile() {
try (FileInputStream fis = new FileInputStream(file)) {
ObjectInputStream ois = new ObjectInputStream(fis);
list = (ArrayList<Express>) ois.readObject(); // 反序列化读取对象
ois.close(); // 关闭输入流
} catch (IOException | ClassNotFoundException e) {
list = new ArrayList<Express>(); // 打开文件异常时 将expresses初始为空
}
}
/**
* 序列化存储对象HashMap<Integer, Express> data
*
* @throws IOException
*/
public void writeToFile() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(list); // 序列化对象
oos.close();
}
/**
* 随机生成6位数取件码
*
* @return 6位数取件码
*/
public int randomCode() {
while (true) {
int code = random.nextInt(900000) + 100000;
Express e = findByCode(code);
if (e == null) {
return code;
}
}
}
/**
* 初始化快递柜位置
*
* @return
*/
public int randomTarget() {
int target;
do {
target = random.nextInt(max) + 1;
} while (findByTarger(target) != null);
return target;
}
/**
* 判断快递柜是否占用
*
* @param target
* @return
*/
private Express findByTarger(int target) {
for (Express express : list) {
if (express.getTarget() == target) {
return express;
}
}
return null;
}
/**
* 根据取件码查询快递
*
* @param code 取件码
* @return 查询的结果,没有则返回null
*/
public Express findByCode(int code) {
for (Express express : list) {
if (express.getCode() == code) {
return express;
}
}
return null;
}
/**
* 根据单号查询
*
* @param order
* @return
*/
public Express findByOrder(String order) {
for (Express e : list) {
if (e.getOrder().equals(order)) {
return e;
}
}
return null;
}
@Override
public synchronized Express add(Express express) {
if (size() >= max) {
return null;
} else {
express.setCode(randomCode());
express.setTarget(randomTarget());
list.add(express);
return express;
}
}
@Override
public synchronized boolean delete(String order) {
Express byOrder = findByOrder(order);
if (byOrder == null) {
return false;
}
list.remove(byOrder);
return true;
}
@Override
public synchronized Express update(Express oldExp, Express newExp) {
Express byOrder = findByOrder(oldExp.getOrder());
if (byOrder != null) {
list.remove(oldExp);
newExp.setCode(randomCode());
newExp.setTarget(randomTarget());
list.add(newExp);
return newExp;
}
return oldExp;
}
@Override
public synchronized ArrayList<Express> getAll() {
return list;
}
@Override
public synchronized boolean getExpress(int code) {
Express express = findByCode(code);
if (express != null) {
list.remove(express);
return true;
}
return false;
}
@Override
public int size() {
return list.size();
}
}
3.3 ExpressService服务器
package service;
import bean.Express;
import dao.ExpressDao;
import dao.impl.ExpressDaoImpl;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class ExpressService {
private ServerSocket serverSocket;
private static ExpressDao expressDao = new ExpressDaoImpl();
private int numOfClient = 0;
public static void main(String[] args) throws IOException {
ExpressService service = new ExpressService();
service.start();
}
public void start() {
try {
serverSocket = new ServerSocket(7063);
System.out.println("服务器已启动");
expressDao.readFromFile();
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();
}
}
}
public void receive(Socket socket) throws IOException {
InputStream is = null;
OutputStream os = null;
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try {
is = socket.getInputStream();
os = socket.getOutputStream();
ois = new ObjectInputStream(is);
oos = new ObjectOutputStream(os);
o:
while (true) {
System.out.println("进入主菜单");
switch (ois.readInt()) {
case 0:
System.out.println("客户端退出");
expressDao.writeToFile();
break o;
case 1:
deliveryManClient(ois, oos);
break;
case 2:
userClient(ois, oos);
break;
default:
break;
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 管理员客户端
*
* @param oos 发送数据给服务区
* @param ois 从服务器接收数据
* @throws IOException
*/
private void deliveryManClient(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
System.out.println("进入快递员界面");
switch (ois.readInt()) {
case 0:
expressDao.writeToFile();
break;
case 1:
System.out.println("添加快递");
addExpress(ois, oos);
break;
case 2:
System.out.println("删除快递");
deleteExpress(ois, oos);
break;
case 3:
System.out.println("修改快递");
updateExpress(ois, oos);
break;
case 4:
System.out.println("查看快递");
seleteAllExpress(ois, oos);
break;
default:
return;
}
}
/**
* 用户
*
* @param oos 发送数据给服务器
* @param ois 从服务器接收数据
* @throws IOException
* @throws ClassNotFoundException
*/
private void userClient(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
System.out.println("用户取快递界面");
// 根据客户端传过来的取件码 查找快递对象
Express e = expressDao.findByCode(ois.readInt());
oos.writeObject(e);// 向客户端传送查找的对象
oos.flush();
if (e != null) {
//取出快递
expressDao.writeToFile();
oos.writeBoolean(expressDao.getExpress(e.getCode()));
oos.flush();
}
}
/**
* 存储快递
*
* @param oos
* @param ois
*/
private void addExpress(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
//1.从客户端接收快递数据
Express addExpress = (Express) ois.readObject();
System.out.println(addExpress.toString());
//2.根据快递单号判断对应快递是否已存在
Express byOrder = expressDao.findByOrder(addExpress.getOrder());
//判断快递是否存在,不存在则添加快递,为空则代表不存在,添加快递。
if (byOrder == null) {
byOrder = expressDao.add(addExpress);
oos.writeObject(byOrder);
oos.flush();
} else {
oos.writeObject(null);
oos.flush();
}
}
/**
* 删除快递
*
* @param oos
* @param ois
*/
private void deleteExpress(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
//获取到客户端输入的单号
String deleteOrder = (String) ois.readObject();
//判断是否存在
Express byOrder = expressDao.findByOrder(deleteOrder);
//发送快递信息至客户端
oos.writeObject(byOrder);
oos.flush();
//如果快递存在
if (byOrder != null) {
//接收用户确认是否删除
switch (ois.readInt()) {
case 1:
oos.writeBoolean(expressDao.delete(deleteOrder));
oos.flush();
break;
default:
break;
}
}
}
/**
* 修改快递
*
* @param oos
* @param ois
*/
private void updateExpress(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
//得到需要修改的单号
String updateOrber = (String) ois.readObject();
//根据单号查找内容,将查找到的数据发送客户端
Express oldExpress = expressDao.findByOrder(updateOrber);
oos.writeObject(oldExpress);
oos.flush();
if (oldExpress != null) {
//接收客户端回传的更改内容
Express newExpress = (Express) ois.readObject();
//根据新快递内容去库中查找是否存在,存在就代表库中有相同的数据
Express isExit = expressDao.findByOrder(newExpress.getOrder());
if (isExit != null) {
oos.writeBoolean(false);
oos.flush();
} else {
Express update = expressDao.update(oldExpress, newExpress);
//修改成功
oos.writeBoolean(true);
//将修改的值返回客户端
oos.writeObject(update);
oos.flush();
}
}
}
/**
* 查看所有快递
*
* @param oos
* @param ois
*/
private void seleteAllExpress(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
// ArrayList<Express> all = expressDao.getAll();
//views.printExpressAll(all);
ArrayList<Express> list = expressDao.getAll();
Express[] expresses = new Express[list.size()];
list.toArray(expresses);
oos.writeObject(expresses);
oos.flush();
}
}
3.4ExpressClient客户端
import bean.Express;
import dao.ExpressDao;
import dao.impl.ExpressDaoImpl;
import views.Views;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ExpressClient {
//初始化视图
private static Views views = new Views();
//初始化Dao对象
private static ExpressDao expressDao = new ExpressDaoImpl();
private static Socket socket;
//状态码
private boolean status = true;
public static void main(String[] args) throws IOException {
ExpressClient ec = new ExpressClient();
ec.init();
}
private void init() throws IOException {
OutputStream os = null;
InputStream is = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
//1.欢迎
views.welcome();
try {
socket = new Socket("127.0.0.1", 7063);
os = socket.getOutputStream();
is = socket.getInputStream();
// 由于服务器是先ois后oos 为了保证配对 这里需要顺序调换
oos = new ObjectOutputStream(os);
ois = new ObjectInputStream(is);
m:
while (status) {
int menu = views.menuMain();
oos.writeInt(menu);
oos.flush();
switch (menu) {
case 0:
break m;
case 1:
deliveryManClient(oos, ois);
break;
case 2:
userClient(oos, ois);
break;
default:
break;
}
}
oos.writeInt(0);
oos.flush();
views.exit();
} 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
*/
private void deliveryManClient(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
int menu = views.menuDeliveryMan();
oos.writeInt(menu);
oos.flush();
switch (menu) {
case 0:
status = false;
break;
case 1:
addExpress(oos, ois);
break;
case 2:
deleteExpress(oos, ois);
break;
case 3:
updateExpress(oos, ois);
break;
case 4:
seleteAllExpress(oos, ois);
break;
default:
break;
}
}
/**
* 用户
*
* @param oos 发送数据给服务器
* @param ois 从服务器接收数据
* @throws IOException
* @throws ClassNotFoundException
*/
private void userClient(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
//获取用户输入的取件码
int code = views.menuUser();
oos.writeInt(code);
oos.flush();
//根据取件码查询快递
Express byCode = (Express) ois.readObject();
if (byCode == null) {
//不存在,提示用户取件码错误或快递不存在
views.printCodeNull();
} else {
//提示用户操作成功,打印快递信息
views.printSuccess();
views.printExpress(byCode);
//从快递柜删除该快递
if (!ois.readBoolean()) views.printFail();
}
}
/**
* 存储快递
*
* @param oos
* @param ois
*/
private void addExpress(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
//1.提示输入快递信息
Express addExpress = views.insert();
oos.writeObject(addExpress);
oos.flush();
//2.判断快递是否已存储
Express add = (Express) ois.readObject();
if (add != null) {
views.printExpress(add);
} else {
//提示快递已存在
views.expressExist();
}
}
/**
* 删除快递
*
* @param oos
* @param ois
*/
private void deleteExpress(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
//获取到用户输入的单号
String deleteOrder = views.findByOrder();
//传给服务器
oos.writeObject(deleteOrder);
//刷新
oos.flush();
//查询单号是否存在
Express i = (Express) ois.readObject();
if (i == null) {
views.printNull();
} else {
//向用户确认是否删除
int menu = views.deleteExpress();
//传给服务器
oos.writeInt(menu);
oos.flush();
switch (menu) {
case 1:
if (ois.readBoolean()) {
views.printSuccess();
} else {
views.printFail();
}
break;
default:
break;
}
}
}
/**
* 修改快递
*
* @param oos
* @param ois
*/
private void updateExpress(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
//得到需要修改的单号
String updateOrber = views.findByOrder();
oos.writeObject(updateOrber);
oos.flush();
//得到服务端回传的数据
Express oldExpress = (Express) ois.readObject();
//判断是否存在
if (oldExpress != null) {
Express newExpress = views.updateExpress();
//将修改的内容发送至服务器
oos.writeObject(newExpress);
oos.flush();
if (!ois.readBoolean()) {
views.expressExist();
views.printExpress(oldExpress);
}else{
//得到修改后的值,打印输出
Express update=(Express) ois.readObject();
views.printSuccess();
views.printExpress(update);
}
} else {
views.printNull();
}
}
/**
* 查看所有快递
*
* @param oos
* @param ois
*/
private void seleteAllExpress(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
// ArrayList<Express> all = expressDao.getAll();
//views.printExpressAll(all);
Express[] es = (Express[]) ois.readObject();
List<Express> expresses = Arrays.asList(es);
if (expresses.size() != 0) {
for (Express ex : expresses) {
views.printExpress(ex);
}
} else {
views.isNull();
}
}
}
3.5 Views视图
package views;
import bean.Express;
import java.util.ArrayList;
import java.util.Scanner;
public class Views {
private Scanner scanner = new Scanner(System.in);
public void welcome() {
System.out.println("=====欢迎使用新职课快递柜=====");
}
public void exit() {
System.out.println("=====欢迎下次使用=====");
}
/**
* 选择身份界面
*
* @return
*/
public int menuMain() {
System.out.println("请输入您的身份:1-快递员,2-用户,0-退出程序");
//全局使用nextLine(),不会因为输入产生冲突,且可以更好的接收各种类型的数据
String input = scanner.nextLine();
int num = -1;
// 判断用户输入是否正确
try {
num = Integer.parseInt(input);
} catch (NumberFormatException e) {
}
if (num < 0 || num > 2) {
System.out.println("输入有误,请重新输入");
return menuMain();
}
return num;
}
/**
* 快递员功能界面
*
* @return
*/
public int menuDeliveryMan() {
System.out.println("请选择操作:1-快递录入,2-快递删除,3-快递修改,4-查看所有快递,5-返回上级目录,0-退出");
String input = scanner.nextLine();
int num = -1;
// 判断用户输入是否正确
try {
num = Integer.parseInt(input);
} catch (NumberFormatException e) {
}
if (num < 0 || num > 5) {
System.out.println("输入有误,请重新输入");
return menuDeliveryMan();
}
return num;
}
/**
* 快递员录入快递
*
* @return 快递单号及快递公司
*/
public Express insert() {
System.out.println("请输入快递单号:");
String order = scanner.nextLine();
System.out.println("请输入快递公司:");
String compony = scanner.nextLine();
Express express = new Express();
express.setOrder(order);
express.setCompony(compony);
return express;
}
/**
* 用于查找快递单号
*
* @return 快递单号
*/
public String findByOrder() {
System.out.println("请输入要操作的快递单号:");
String order = scanner.nextLine();
return order;
}
/**
* 打印快递信息
*
* @param express
*/
public void printExpress(Express express) {
System.out.println(express.toString());
}
/**
* 修改快递信息
*
* @param
*/
public Express updateExpress() {
System.out.println("请输入新的快递单号:");
String order = scanner.nextLine();
System.out.println("请输入新的快递公司:");
String compony = scanner.nextLine();
Express express =new Express();
express.setOrder(order);
express.setCompony(compony);
return express;
}
/**
* 询问是否删除
*
* @return 1确认删除,2取消并退出
*/
public int deleteExpress() {
System.out.println("是否确认删除:1-确认删除,其他-取消操作");
String input = scanner.nextLine();
int num = -1;
// 判断用户输入是否正确
try {
num = Integer.parseInt(input);
} catch (NumberFormatException e) {
}
/* if (num < 1) {
System.out.println("输入有误,请重新输入");
return deleteExpress();
}*/
return num;
}
/**
* 将给定数组的快递信息,遍历显示
*
* @param expresses
*/
public void printExpressAll(ArrayList<Express> expresses) {
int count = 0;
for (int i = 0; i < expresses.size(); i++) {
count++;
System.out.println(expresses.get(i));
}
if (count == 0) {
System.out.println("暂无快递信息");
}
}
/**
* 用户菜单界面
*
* @return
*/
public int menuUser() {
System.out.println("请输入取件码:");
String code = scanner.nextLine();
int num = -1;
// 判断用户输入是否正确
try {
num = Integer.parseInt(code);
} catch (NumberFormatException e) {
}
if (num < 100000 || num > 999999) {
System.out.println("输入有误,请重新输入");
return menuUser();
}
return num;
}
/**
* 提示快递已存在
*/
public void expressExist() {
System.out.println("此单号在快递柜中已存在,请检查是否正确");
}
/**
* 打印空信息,提示快递不存在
*/
public void printNull() {
System.out.println("该快递不存在,请检查输入是否正确!");
}
/**
* 打印操作成功信息
*/
public void printSuccess() {
System.out.println("操作成功!");
}
/**
* 打印空取件码,提示取件码错误
*/
public void printCodeNull() {
System.out.println("取件码错误或快递已取出,请检查!");
}
public void printFail() {
System.out.println("操作失败!");
}
public void isNull(){
System.out.println("无快递信息");
}
}
4.注意事项
1.客户端与服务器收发必须对应,客户端发送,服务器接收。服务器发送,客户端接收。即ObjectInputStream与ObjectOutputStream一一对应!!
2.在使用修改方法时,如果使用list.remove(oldExp);return add(newExp),方法会导致update方法调用add线程方法,卡住程序。(不敢确定,没有深入分析)。
5.疑问
在客户端发送菜单指令后,服务器应该是一一对应的,但服务器的中break语句正常结束后,ois.readInt()方法不会返回,而是一直往下执行。必须使用return才能返回上级。----已解决
6.解决问题
1.在服务器中仅监听客户端连接及主菜单选择处需要使用while(true)循环,无需与客户端一致。
2.在修改快递时,需要判断修改后的快递是否存在,如果已存在则需提醒客户修改失败。