day25綜合案例-學員管理系統【C/S版】
反饋和複習
1.建議下午也上課(9-12 2-3)
2."正則反向原理"
複習:
1.枚舉
public enum 枚舉名{
枚舉項1,枚舉項2,...枚舉項n;
}
===>
public enum Sex{
BOY,GIRL;
}
本質:
public final class Sex extends java.lang.Enum<Sex>{
//枚舉 項,其實就是當前類的一個靜態對象
public static final Sex BOY = new Sex();
public static final Sex GIRL = new Sex();
//類中其實也可以添加我們自己的成員變量/成員方法/構造方法
}
2.JDK剩下的2個特性
方法引用: 把已經存在的方法,直接拿過來,給函數式接口類型的變量賦值
System.out::println
集合.foreach(System.out::println)
流.foreach(System.out::println)
Base64編碼:
Decoder(解碼器) Encoder(編碼器)
3.正則表達式
普通字符串.matches(正則表達式);
普通字符串.split(正則表達式);
普通字符串.replaceAll(正則表達式,新的字符串);
正則的語法:
字符類: [abc][^abc][0-9][a-z][A-Z][0-9a-zA-Z][a-em-q]
邏輯運算符類: && |
預定義字符: \d \w .
數量詞: A* A? A+ A{n} A{n,} A{n,m}
分組: (..){2}
今日內容
學生管理系統(C/S)
用戶在客戶端操作(增刪改查)
通過TCP編程把數據發送給服務器
服務器獲取數據後要把數據保存到本地
通過TCP編程把結果反饋給客戶端
一 項目演示
1.1 打開項目
1.2 運行項目
- 運行服務器
- 運行客戶端
1.3 增刪改查演示
-
添加學生
-
根據id查詢學生
- 查詢所有學生
- 修改學生
- 刪除學生
二 項目說明
2.1 所採用的知識點要求
-
IO流技術
服務器讀寫本地文件時,我們採用普通字符流/緩衝流技術, 要求一個學生一行,並且學生的屬性之間使用","間隔 比如: 1,張三,男,18 2,李四,女,28
-
網絡編程TCP技術
客戶端和服務器採用TCP"短連接",每個功能當需要與服務器連接時,才建立連接,功能完畢,連接立即 斷開,即客戶端使用一個功能連接服務器一次,使用完畢斷開連接
-
多線程技術
爲了同時支持多個客戶端,可以連接服務器,要求每個客戶端來了,服務器都要開啓一個新的線程處理
-
序列化和反序列化技術
服務器給客戶端返回一個學生對象或者一個學生集合時,我們採用序列化流發送給客戶端,客戶端採用反序列化流讀取數據
2.2 客戶端與服務器交互
- 交互過程圖解
-
數據格式說明
添加:"[1]數據",例如:"[1]張三,男,22" 根據id查詢一條數據:"[2]id",例如:"[2]1",意思:查詢id爲1的學員信息 修改一條數據:"[3]新數據"。例如:"[3]1,張三2,女,19",意思:將id=1的學員改爲後面的新數據。 查詢所有數據:"[4]"。例如:"[4]",意思:後面不用帶任何數據。 刪除一條數據:"[5]id"。例如:"[5]1",意思:刪除id爲1的記錄
三 客戶端代碼實現
3.1 學生類和打印學生信息的工具類介紹
學生類:
public class Student implements Serializable{
private int id;
private String name;
private String sex;
private int age;
//其他省略
}
工具類:
public class StudentUtils {
//打印ArrayList<Student>的方法
public static void printStudentList(ArrayList<Student> stuList) {
System.out.println("--------------------------------------------------");
System.out.println("編號\t\t姓名\t\t\t性別\t\t年齡");
for (int i = 0; i < stuList.size(); i++) {
Student stu = stuList.get(i);
System.out.println(stu.getId() + "\t\t" + stu.getName() + "\t\t\t" + stu.getSex() + "\t\t" + stu.getAge());
}
System.out.println("--------------------------------------------------");
}
//打印單個學生的方法
public static void printStudent(Student stu) {
System.out.println("--------------------------------------------------");
System.out.println("編號\t\t姓名\t\t\t性別\t\t年齡");
System.out.println(stu.getId() + "\t\t" + stu.getName() + "\t\t\t" + stu.getSex() + "\t\t" + stu.getAge());
System.out.println("--------------------------------------------------");
}
}
3.2 編寫客戶端主程序
public class StudentClient {
//定義一個靜態的Scanner對象
public static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("歡迎使用學生管理系統~~~");
while (true) {
//1.菜單
System.out.println();//空行,爲了美觀
System.out.println("1.添加學生 2.修改學生 3.刪除學生 4.查詢學生 5.查看所有學生 6.退出");
//2.鍵盤錄入
System.out.println("請選擇:");
int user = sc.nextInt();
//3.判斷
switch (user) {
case 1:
//添加學生
addStudent();
break;
case 2:
//修改學生
updateStudent();
break;
case 3:
//刪除學生
deleteStudent();
break;
case 4:
//根據ID查詢
findStudent();
break;
case 5:
//查詢所有學生
findStudents();
break;
case 6:
//退出
System.out.println("歡迎下次光臨~~");
System.exit(0);
default:
//提示輸入有誤
System.out.println("您輸入的功能有誤~~");
break;
}
}
}
}
3.3 添加學生功能
1.客戶端得到數據(用戶輸入)
2.使用TCP連接將數據發送給服務器
3.讀取服務器發送回來的反饋
4.釋放資源
//添加學生
public static void addStudent() {
System.out.println("【添加學生】");
//1.客戶端得到數據(用戶輸入)
System.out.println("請輸入姓名:");
String name = sc.next();
System.out.println("請輸入性別:");
String sex = sc.next();
System.out.println("請輸入年齡:");
int age = sc.nextInt();
//2.使用TCP連接將數據發送給服務器
Socket socket = getSocket();
if (socket == null) {
System.out.println("服務器暫時無法連接...");
return;
}
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();) {
//給服務器發送數據的格式:"[1]數據",例如:"[1]張三,男,22"
out.write(("[1]" + name + "," + sex + "," + age).getBytes());
//3.讀取服務器發送回來的反饋
int result = in.read();
if (result == 1) {
System.out.println("【添加成功】");
} else {
System.out.println("【添加失敗,請聯繫系統管理員~~~】");
}
//4.釋放資源
socket.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}
3.4 修改學生功能
//修改學生
public static void updateStudent() {
System.out.println("【修改學生】");
//1.客戶端得到數據(用戶輸入)
System.out.println("請輸入要修改學生的ID:");
int ID = sc.nextInt();
//我們修改學生之前,先要查詢該學生,如果不存在,提示不存在,如果存在,再讓用戶輸入其他修改後的信息
//2.使用TCP連接將數據發送給服務器
Socket socket = getSocket();
if (socket == null) {
System.out.println("服務器暫時無法連接...");
return;
}
Student student = null;
//獲取輸出流
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();) {
//查詢學生的格式:"[2]id",例如:"[2]1"
out.write(("[2]" + ID).getBytes());
//3.讀取服務器發送回來的反饋
ObjectInputStream ois = new ObjectInputStream(in);
student = (Student) ois.readObject();
if (student == null) {
System.out.println("【查無此人】");
return;
} else {
StudentUtils.printStudent(student);
}
//4.釋放資源
socket.close();
ois.close();
} catch (Exception ie) {
ie.printStackTrace();
}
//我們修改學生之前,先要查詢該學生,如果不存在,提示不存在,如果存在,再讓用戶輸入其他修改後的信息
//程序執行到這,說明有此學生,我們就可以修改
//1.客戶端得到數據(用戶輸入其他的信息)
System.out.println("請輸入新的姓名(如果不修改輸入0):");
String name = sc.next();
System.out.println("請輸入新的性別(如果不修改輸入0):");
String sex = sc.next();
System.out.println("請輸入新的年齡(如果不修改輸入0):");
int age = sc.nextInt();
//用戶輸入完畢之後,我們先判斷
if (!"0".equals(name)) {
student.setName(name);
}
if (!"0".equals(sex)) {
student.setSex(sex);
}
if (age != 0) {
student.setAge(age);
}
//2.使用TCP連接將數據發送給服務器
Socket socketUpdate = getSocket();
if (socketUpdate == null) {
System.out.println("服務器暫時無法連接...");
return;
}
try (OutputStream out = socketUpdate.getOutputStream();
InputStream in = socketUpdate.getInputStream();) {
//發送修改信息數據格式: "[3]新數據"。例如:"[3]1,張三2,女,19"
out.write(("[3]" + ID + "," + student.getName() + "," + student.getSex() + "," + student.getAge()).getBytes());
//3.讀取服務器發送回來的反饋
int result = in.read();
if (result == 1) {
System.out.println("【修改成功】");
} else {
System.out.println("【修改失敗,請聯繫管理員~~~】");
}
//4.釋放資源
socketUpdate.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}
3.5 根據id查詢學生功能
//根據ID查詢
public static void findStudent() {
System.out.println("【根據ID查詢】");
//1.客戶端得到數據(用戶輸入)
System.out.println("請輸入要查詢的學生的ID:");
int ID = sc.nextInt();
//2.使用TCP連接將數據發送給服務器
Socket socket = getSocket();
if (socket == null) {
System.out.println("服務器暫時無法連接...");
return;
}
//獲取輸出流
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();) {
//查詢學生的格式:"[2]id",例如:"[2]1"
out.write(("[2]" + ID).getBytes());
//3.讀取服務器發送回來的反饋
ObjectInputStream ois = new ObjectInputStream(in);
Student student = (Student) ois.readObject();
if (student == null) {
System.out.println("【查無此人】");
} else {
StudentUtils.printStudent(student);
}
//4.釋放資源
socket.close();
ois.close();
} catch (Exception ie) {
ie.printStackTrace();
}
}
3.6 查詢所有學生功能
//查詢所有學生
public static void findStudents() {
System.out.println("【查詢所有學生】");
//1.客戶端得到數據(用戶輸入)
//客戶端不需要輸入任何東西
//2.使用TCP連接將數據發送給服務器
Socket socket = getSocket();
if (socket == null) {
System.out.println("服務器暫時無法連接...");
return;
}
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();) {
//查詢所有的格式:"[4]"。例如:"[4]"
out.write("[4]".getBytes());
//3.讀取服務器發送回來的反饋
ObjectInputStream ois = new ObjectInputStream(in);
ArrayList<Student> students = (ArrayList<Student>) ois.readObject();
if (students == null || students.size() == 0) {
System.out.println("【服務器暫無學生信息】");
} else {
//調用工具類
StudentUtils.printStudentList(students);
}
//4.釋放資源
socket.close();
} catch (Exception ie) {
ie.printStackTrace();
}
}
3.6 刪除學生功能
//刪除學生
public static void deleteStudent() {
System.out.println("【刪除學生】");
//1.客戶端得到數據(用戶輸入)
System.out.println("請輸入您要刪除學生的ID:");
int ID = sc.nextInt();
//用戶輸入完畢ID之後,我們不應該立刻發送給服務器進行刪除
//應該先把該ID的學生查詢出來,展示給我用戶,然後問他真的確認刪除嗎
//2.使用TCP連接將數據發送給服務器
Socket socket = getSocket();
if (socket == null) {
System.out.println("服務器暫時無法連接...");
return;
}
Student student = null;
//獲取輸出流
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();) {
//查詢學生的格式:"[2]id",例如:"[2]1"
out.write(("[2]" + ID).getBytes());
//3.讀取服務器發送回來的反饋
student = (Student) ois.readObject();
if (student == null) {
System.out.println("【查無此人】");
return;
} else {
StudentUtils.printStudent(student);
}
//4.釋放資源
socket.close();
ois.close();
} catch (Exception ie) {
ie.printStackTrace();
}
//用戶輸入完畢ID之後,我們不應該立刻發送給服務器進行刪除
//應該先把該ID的學生查詢出來,展示給我用戶,然後問他真的確認刪除嗎
//程序執行到此處,說明有該ID的學生
//1.客戶端得到數據(用戶輸入確定信息)
System.out.println("您確定要刪除以上信息的學生嗎(y/n)?");
String user = sc.next();
if (!"y".equals(user)) { //只要用戶輸入的不是y 我們就認爲取消!!
System.out.println("【刪除操作已經取消】");
return;
}
//2.使用TCP連接將數據發送給服務器
Socket socketDelete = getSocket();
if (socketDelete == null) {
System.out.println("服務器暫時無法連接...");
return;
}
try (OutputStream out = socketDelete.getOutputStream();
InputStream in = socketDelete.getInputStream();) {
//發送刪除數據格式:"[5]id"。例如:"[5]1"
out.write(("[5]" + ID).getBytes());
//3.讀取服務器發送回來的反饋
int result = in.read();
if (result == 1) {
System.out.println("【刪除成功】");
} else {
System.out.println("【刪除失敗,請聯繫管理員~~】");
}
//4.釋放資源
socketDelete.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}
四 服務器代碼實現
4.1 學生類和保存學生數據的工具類介紹
學生類:
public class Student implements Serializable{
private int id;
private String name;
private String sex;
private int age;
//其他省略
}
保存學生數據的工具類
public class StudentDao {
//將集合中所有學生對象,寫入到文件中
public static void writeAll(ArrayList<Student> stuList) {
try (FileWriter out = new FileWriter("student.txt")) {
for (Student stu : stuList) {
//格式: id,姓名,性別,年齡
out.write(stu.getId() + "," + stu.getName() + "," + stu.getSex() + "," + stu.getAge());
//換行
out.write("\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
//從文件中讀取所有學生的信息,返回學生集合
public static ArrayList<Student> readAll() {
ArrayList<Student> stuList = new ArrayList<>();
//1.創建File對象
File file = new File("student.txt");
if (!file.exists()) {
try {
//2.如果不存在,則創建,否則讀取會拋異常
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//3.讀數據,一次一行 格式: id,姓名,性別,年齡
try (BufferedReader bufIn = new BufferedReader(
new FileReader("student.txt"))) {
String line = null;
while ((line = bufIn.readLine()) != null) {
//4.一行切割成一個數組,[id,姓名,性別,年齡]
String[] rowArray = line.split(",");
//5.創建學生對象,封裝數據
Student stu = new Student();
stu.setId(Integer.parseInt(rowArray[0]));
stu.setName(rowArray[1]);
stu.setSex(rowArray[2]);
stu.setAge(Integer.parseInt(rowArray[3]));
//6.添加到集合中
stuList.add(stu);
}
} catch (IOException e) {
return null;
}
//7.返回整個集合
return stuList;
}
//添加一個學生,返回boolean代表是否添加成功
public static boolean addStudent(Student student) {
//1.先讀取所有學生
ArrayList<Student> stuList = readAll();
if (stuList == null) {//說明讀取文件出錯
return false;
}
//2.獲取最後一個學生的id,加1後作爲新學生的id
if (stuList.size() != 0) {
student.setId(stuList.get(stuList.size() - 1).getId() + 1);//取最後一個對象的id + 1
} else {
//3.如果沒有學生,說明是第一個,則id設置爲1
student.setId(1);//第一次添加,文件中沒有內容
}
//4.添加到集合中
stuList.add(student);
//5.把集合重寫寫入到文件中
writeAll(stuList);
//6.返回添加成功
return true;
}
//根據id刪除一個學生,返回boolean代表是否刪除成功
public static boolean deleteById(int id) {
//1.先讀取所有學生
ArrayList<Student> stuList = readAll();
if (stuList == null) {//說明讀取文件出錯
return false;
}
//2.遍歷集合
for (int i = 0; i < stuList.size(); i++) {
Student stu = stuList.get(i);
//3.判斷學生的id是否和要刪除的id相等
if (stu.getId() == id) {
//4.從集合中刪除學生
stuList.remove(i);
//5.重寫寫入到文件中
writeAll(stuList);
//6.返回成功
return true;
}
}
//7.如果沒找到學生返回失敗
return false;
}
//修改學生,返回boolean代表是否修改成功
public static boolean updateStudent(Student student) {
//1.先讀取所有學生
ArrayList<Student> stuList = readAll();
if (stuList == null) {//說明讀取文件出錯
return false;
}
System.out.println("修改的數據:" + student);
//2.遍歷集合
for (int i = 0; i < stuList.size(); i++) {
Student stu = stuList.get(i);
//3.判斷哪個學生id和要修改的學生id相同
if (stu.getId() == student.getId()) {
//4.將學生改爲新的學生
stuList.set(i, student);
//5.重寫將集合寫入到文集中
writeAll(stuList);//寫回文件
//6.返回成功
return true;
}
}
//7.返回失敗
return false;//沒找到
}
//根據id查詢學生,返回查詢到的學生
public static Student findById(int id) {
//1.先讀取所有學生
ArrayList<Student> stuList = readAll();
if (stuList == null) {//說明讀取文件出錯
return null;
}
//2.遍歷集合
for (int i = 0; i < stuList.size(); i++) {
Student stu = stuList.get(i);
//3.比較id
if (stu.getId() == id) {
//4.找到返回學生對象
return stu;
}
}
//5.找不到返回null
return null;
}
}
4.2 編寫服務器主程序
注意我們的需求: 每個客戶端連接後要開啓一個線程來處與這個客戶端的數據交互!!
public class StudentServer {
public static void main(String[] args) throws IOException {
//1.啓動
ServerSocket server = new ServerSocket(8888);
System.out.println("學生管理系統服務器啓動...");
//2.等待客戶端連接
while (true) {
System.out.println("等待客戶端...");
Socket socket = server.accept();
//3.開啓一個線程,傳入Socket對象
new ServerThread(socket).start();
}
}
}
public class ServerThread extends Thread {
public Socket socket;
public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
}
}
4.3 編寫線程的任務代碼
public class ServerThread extends Thread {
public Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//1.讀取客戶端發送的數據
try (InputStream in = socket.getInputStream();) {
byte[] bs = new byte[1024];
int len = in.read(bs);
String msg = new String(bs, 0, len);
System.out.println("客戶端的數據:" + msg);
//2.分析數據
//合法性判斷
if (msg.charAt(0) != '[' || msg.charAt(2) != ']') {
System.out.println("客戶端數據格式有誤~~~");
socket.close();
in.close();
return;
}
//截取數據
String num = msg.substring(1, 2);//[1]再趕,男,30
//3.判斷
switch (num) {
case "1":
//添加學生
addStudent(msg);
break;
case "2":
//根據ID查詢
findStudent(msg);
break;
case "3":
//修改數據
updateStudent(msg);
break;
case "4":
//查詢所有
findStudents(msg);
break;
case "5":
//刪除一條
deleteStudent(msg);
break;
default:
//客戶端數據有誤~~
System.out.println("該客戶端數據有誤~~");
socket.close();
break;
}
} catch (IOException ie) {
ie.printStackTrace();
}
}
}
4.4 線程中的增刪改查實現
思考:服務器增刪改查的方法,有沒有共同的步驟
1.封裝數據
2.調用StudentDao的增刪改查方法
3.反饋數據給客戶端
4.釋放資源
-
添加學生
//添加學生 public void addStudent(String msg) { //msg [1]再趕,男,30 //1.封裝數據 String stuMsg = msg.substring(3); String[] msgArr = stuMsg.split(",");//[再趕,男,30] Student newStudent = new Student(msgArr[0],msgArr[1],Integer.parseInt(msgArr[2])); //2.調用StudentDao的增刪改查方法 StudentDao.addStudent(newStudent); //3.反饋數據給客戶端 try (OutputStream out = socket.getOutputStream();) { out.write(1); //4.釋放資源 socket.close(); } catch (IOException ie) { ie.printStackTrace(); } }
-
查詢所有學生
//查詢所有學生 public void findStudents(String msg) { //1.封裝數據 //不需要封裝任何數據 //2.調用StudentDao的增刪改查方法 ArrayList<Student> stuList = StudentDao.readAll(); //3.反饋數據給客戶端 try { OutputStream out = socket.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(stuList); //4.釋放資源 oos.close(); out.close(); socket.close(); } catch (IOException ie) { ie.printStackTrace(); } }
-
根據id查詢
//根據ID查詢學生 public void findStudent(String msg) { //msg: "[2]1" //1.封裝數據 int findID = Integer.parseInt(msg.substring(3)); //2.調用StudentDao的增刪改查方法 Student student = StudentDao.findById(findID); //3.反饋數據給客戶端 try { OutputStream out = socket.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(student); //4.釋放資源 oos.close(); out.close(); socket.close(); } catch (IOException ie) { ie.printStackTrace(); } }
-
修改學生
//修改學生 public void updateStudent(String msg) { //msg: [3]1,張三2,女,19 //1.封裝數據 String msgStu = msg.substring(3); String[] msgArr = msgStu.split(",");//[1,張三2,女,19] Student s = new Student(); s.setId(Integer.parseInt(msgArr[0])); s.setName(msgArr[1]); s.setSex(msgArr[2]); s.setAge(Integer.parseInt(msgArr[3])); //2.調用StudentDao的增刪改查方法 StudentDao.updateStudent(s); //3.反饋數據給客戶端 try { OutputStream out = socket.getOutputStream(); out.write(1); //4.釋放資源 out.close(); socket.close(); } catch (IOException ie) { ie.printStackTrace(); } }
-
根據id刪除
//根據ID刪除學生 public void deleteStudent(String msg) { //msg: [5]1 //1.封裝數據 int deleteID = Integer.parseInt(msg.substring(3)); //2.調用StudentDao的增刪改查方法 StudentDao.deleteById(deleteID); //3.反饋數據給客戶端 try { OutputStream out = socket.getOutputStream(); out.write(1); //4.釋放資源 out.close(); socket.close(); } catch (IOException ie) { ie.printStackTrace(); } }
總結
能夠完成客戶端添加功能
[1]name,sex,age
能夠完成客戶端修改功能
[3]id,name,sex,age
能夠完成客戶端刪除功能
[5]id
能夠完成客戶端獲取功能
[2]id
[4]
能夠完成服務端功能
根據客戶端的編碼,完成不同的操作