今日内容
学员管理系统
学习目标
能够完成客户端添加功能 能够完成客户端修改功能 能够完成客户端删除功能 能够完成客户端获取功能 能够完成服务端功能
一 项目演示
1 打开项目
目录结构如下图:
2 运行项目
运行服务器端:
注:服务器使用端口:8888
运行客户端:
执行添加、修改、删除、查询:
注:保存学生信息的文件user.txt存放到项目根目录
二 项目说明
1 所采用的知识点
本系统采用了我们学过的以下几个核心知识点:
1). IO流技术
2). 网络编程技术
3). 序列化
4). 多线程
2 业务交互模式图示
注意:
1.客户端向服务器端发送的操作(增删改查)的序号使用[]括起来了,不括起来也可以
2.对于删 改和根据编号id查询一个人的信息,都是客户端向服务器端发送的是学员学号即id.
【说明】
1).客户端和服务器端采用TCP连接;
2).数据保存在服务器端;
3). 客户端增删改查发送数据格式说明:
a). 添加:"[1]数据",例如:"[1]张三,男,22",意思:没有id字段,由服务器端在写入数据前自动添加。
b). 修改一条数据:"[3]新数据"。例如:"[3]1,张三2,女,19",意思:将id=1的学员改为后面的新数据。
c). 查询所有数据:"[4]"。例如:"[4]",意思:后面不用带任何数据。
d). 删除一条数据:"[5]id"。例如:"[5]1",意思:删除id为1的记录。
三 案例代码
1.项目包结构
2.实体类(Student类)
package com
.itheima
.sh
.domain
;
import java
.io
.Serializable
;
public class Student implements Serializable {
public static int idCount
= 1;
{
idCount
++;
}
private int id
;
private String name
;
private int age
;
private String address
;
public Student() {
}
public Student(int id
, String name
, int age
, String address
) {
this.id
= id
;
this.name
= name
;
this.age
= age
;
this.address
= address
;
}
public int getId() {
return id
;
}
public void setId(int id
) {
this.id
= id
;
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public int getAge() {
return age
;
}
public void setAge(int age
) {
this.age
= age
;
}
public String
getAddress() {
return address
;
}
public void setAddress(String address
) {
this.address
= address
;
}
@Override
public String
toString() {
return "Student{" +
"id=" + id
+
", name='" + name
+ '\'' +
", age=" + age
+
", address='" + address
+ '\'' +
'}';
}
}
说明:
1.因为学生对象要被序列化到硬盘文件中,所以Student类需要实现序列化接口Serializable
3.客户端主界面搭建
public class DemoCilent {
public static void main(String
[] args
) throws Exception
{
Socket s
= new Socket("127.0.0.1",8888);
Scanner sc
= new Scanner(System
.in
);
while(true){
System
.out
.println("请选择你要完成的操作:");
System
.out
.println("1. 增加学生 2.删除学生 3.修改学生 4.查看所有学生 5.退出");
System
.out
.println("------------------------------------------------------");
int choose
= sc
.nextInt();
switch (choose
){
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
s
.close();
return;
}
}
}
}
说明:因为还没有服务器,所以想运行客户端需要去掉连接服务器的代码
Socket s
= new Socket("127.0.0.1",8888);
4.添加功能
4.1客户端
private static void add(Socket s
) throws IOException
{
Scanner sc
= new Scanner(System
.in
);
System
.out
.println("请输入要添加的学生姓名:");
String name
= sc
.next();
System
.out
.println("请输入要添加的学生年龄");
int age
= sc
.nextInt();
System
.out
.println("请输入要添加的学生居住地");
String address
= sc
.next();
String str
= "[1]"+name
+","+age
+","+address
;
OutputStream out
= s
.getOutputStream();
out
.write(str
.getBytes());
InputStream in
= s
.getInputStream();
byte[] bys
= new byte[1024];
int len
= in
.read(bys
);
System
.out
.println(new String(bys
,0,len
));
}
说明:学生id不用输入,在服务器端自动生成。
4.2服务器端
说明:
1.对于服务器端我们需要考虑起初客户端连接服务器的时候,项目下是否含有存储学生对象的文件,如果没有需要创建文件,并将空的list集合放到文件中,否则会报找不到文件异常
2.使用死循环让服务器一直运行,使用多线程为每个客户端开启一个线程
public class DemoServer {
public static void main(String
[] args
) throws Exception
{
ServerSocket ss
= new ServerSocket(8888);
File f
= new File("user.txt");
if (!f
.exists()) {
ObjectOutputStream oos
= new ObjectOutputStream(new FileOutputStream("user.txt"));
ArrayList
<Student> list
= new ArrayList<>();
oos
.writeObject(list
);
}
while (true) {
Socket s
= ss
.accept();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
InputStream in
= s
.getInputStream();
byte[] bys
= new byte[1024];
int len
= in
.read(bys
);
String str
= new String(bys
, 0, len
);
char choose
= str
.charAt(1);
String message
= str
.substring(3);
switch (choose
) {
case '1':
add(message
);
break;
case '2':
break;
case '3':
break;
case '4':
break;
}
} catch (Exception e
) {
}
}
}
private void add(String message
) throws IOException
, ClassNotFoundException
{
String
[] strs
= message
.split(",");
Student stu
= new Student(Student
.idCount
, strs
[0], Integer
.parseInt(strs
[1]), strs
[2]);
ObjectInputStream ois
= new ObjectInputStream(new FileInputStream("user.txt"));
ArrayList
<Student> list
= (ArrayList
<Student>) ois
.readObject();
ObjectOutputStream oos
= new ObjectOutputStream(new FileOutputStream("user.txt"));
list
.add(stu
);
oos
.writeObject(list
);
OutputStream out
= s
.getOutputStream();
out
.write("添加成功".getBytes());
}
}).start();
}
}
}
5.查看功能
5.1客户端
private static void findAll(Socket s
) throws IOException
, ClassNotFoundException
{
OutputStream out
= s
.getOutputStream();
out
.write("[4]".getBytes());
ObjectInputStream ois
= new ObjectInputStream(s
.getInputStream());
ArrayList
<Student> list
= (ArrayList
<Student>)ois
.readObject();
for (Student stu
: list
) {
System
.out
.println(stu
);
}
}
说明:
因为要从和服务器的连接通道中读取对象数据,所以这里的反序列化流关联的是连接通道所以使用s.getInputStream()获取字节输入流。
ObjectInputStream ois
= new ObjectInputStream(s
.getInputStream());
5.2服务器
private void findAll() throws IOException
, ClassNotFoundException
{
ObjectInputStream ois
= new ObjectInputStream(new FileInputStream("user.txt"));
ArrayList
<Student> list
= (ArrayList
<Student>) ois
.readObject();
ObjectOutputStream oos
= new ObjectOutputStream(s
.getOutputStream());
oos
.writeObject(list
);
}
说明:
1.从文件读取集合,所以使用new FileInputStream(“user.txt”)获取字节输入流
2.将集合写到连接通道中,所以使用s.getOutputStream()获取字节输出流
6.删除功能
6.1客户端
private static void remove(Socket s
) throws IOException
{
Scanner sc
= new Scanner(System
.in
);
System
.out
.println("请输入你要删除的学生编号:");
int id
= sc
.nextInt();
String str
= "[2]"+id
;
OutputStream out
= s
.getOutputStream();
out
.write(str
.getBytes());
InputStream in
= s
.getInputStream();
byte[] bys
= new byte[1024];
int len
= in
.read(bys
);
System
.out
.println(new String(bys
,0,len
));
}
6.2服务器端
private void remove(String message
) throws IOException
, ClassNotFoundException
{
ObjectInputStream ois
= new ObjectInputStream(new FileInputStream("user.txt"));
ArrayList
<Student> list
= (ArrayList
<Student>) ois
.readObject();
int id2
= Integer
.parseInt(message
);
OutputStream out
= s
.getOutputStream();
for (int i
= 0; i
< list
.size(); i
++) {
Student stu
= list
.get(i
);
int id
= stu
.getId();
if (id2
== id
) {
list
.remove(i
);
out
.write("删除成功".getBytes());
ObjectOutputStream oos
= new ObjectOutputStream(new FileOutputStream("user.txt"));
oos
.writeObject(list
);
return;
}
}
out
.write("编号不存在".getBytes());
}
7.修改功能
7.1客户端
private static void update(Socket s
) throws IOException
{
Scanner sc
= new Scanner(System
.in
);
System
.out
.println("请输入要修改的学生编号");
int id
= sc
.nextInt();
System
.out
.println("请输入新的姓名:(如果输入0表示保留原值)");
String name
= sc
.next();
System
.out
.println("请输入新的年龄:(如果输入0表示保留原值)");
int age
= sc
.nextInt();
System
.out
.println("请输入新的居住地:(如果输入0表示保留原值)");
String address
= sc
.next();
String str
= "[3]"+id
+","+name
+","+age
+","+address
;
OutputStream out
= s
.getOutputStream();
out
.write(str
.getBytes());
InputStream in
= s
.getInputStream();
byte[] bys
= new byte[1024];
int len
= in
.read(bys
);
System
.out
.println(new String(bys
,0,len
));
}
7.2服务端
private void update(String message
) throws IOException
, ClassNotFoundException
{
String
[] arr
= message
.split(",");
int id2
= Integer
.parseInt(arr
[0]);
ObjectInputStream ois
= new ObjectInputStream(new FileInputStream("user.txt"));
ArrayList
<Student> list
= (ArrayList
<Student>) ois
.readObject();
for (int i
= 0; i
< list
.size(); i
++) {
Student stu
= list
.get(i
);
int id
= stu
.getId();
if (id
== id2
) {
if (!arr
[1].equals("0"))
stu
.setName(arr
[1]);
if (!arr
[2].equals("0"))
stu
.setAge(Integer
.parseInt(arr
[2]));
if (!arr
[3].equals("0"))
stu
.setAddress(arr
[3]);
ObjectOutputStream oos
= new ObjectOutputStream(new FileOutputStream("user.txt"));
oos
.writeObject(list
);
OutputStream out
= s
.getOutputStream();
out
.write("修改成功".getBytes());
return;
}
}
OutputStream out
= s
.getOutputStream();
out
.write("修改失败".getBytes());
}