文章目录
完整版请点击此处!!!
本文为简易版,仅含涉及到多线程与网络编程的方法、且并不完善
包含源码、完善的完整版请阅读文章:加入多线程与网络编程之完整版,附完整源码
数组存储版可参考文章:快递管理控制台简易版——数组存储版(Java)
List集合存储版可参考文章:快递管理控制台简易版——List集合存储版(Java)
Map集合存储版可参考文章:快递管理控制台简易版——Map集合存储版(Java)
基于Map集合存储版使用IO技术实现数据存储可参考文章:快递管理控制台简易版——IO流的使用(Java)
-
数组存储版采用二维数组对快递进行存储,快递位置对应二维数组的两个下标
-
List集合存储版采用List集合对快递进行存储
-
Map集合存储版采用Map集合对快递进行存储,用Map集合进行存储的优势在于,Map是键-值(key-value)对,key为快递单号,value 为快递对象,保证快递单号唯一
-
IO流的使用 以Map集合存储版为基础进行优化,使用IO技术将快递数据存储到文件中,并从文件中读取数据,文件存储快递信息后,可以在每次启动应用时读取文件中的内容,从而实现程序数据的一直存在
-
关于多线程和网络编程加入了多线程和网络编程技术,实现客户端和服务器的数据交互,并且使用多线程,使得每一个用户发送登录请求到服务器后,服务器端就分配一个线程去处理这个请求
tips:本文仅仅涉及到如何加入多线程和运用网络编程技术,不涉及完整的控制台操作
具体需求
- 实现基于网络编程模式下的用户登录功能,要求:
(1)用户包含管理员和普通用户两个角色进行测试
管理员用户名:admin,密码:abc 普通用户名:user 密码:123
(2)用户从客户端发起登录请求,客户端将数据传递到服务器端,
由服务器做验证。服务器端保存用户数据,用户登录成功提示登录成功
并显示功能模块,登录失败提示:用户名或密码不正确 - 基于上一个练习,我们需要了解客户端和服务器端的概念。其次要明白,客户端用来获取用户信息,服务器端用于进行数据存储和逻辑判断。在练习 1 的基础上,在服务器端启用多线程模式,要求:每一个用户发送登录请求到服务器后,服务器端就分配一个线程去处理这个请求,以实现服务器端多线程模式下的用户登录
管理员
- 快递录入
…柜子位置(系统产生,不能重复)
…快递单号(输入)
…快递公司(输入)
…6位取件码(系统产生,不能重复)- 删除快递(根据单号)
- 修改快递(根据单号)
- 查看所有快递(遍历)
普通用户
- 快递取出
…输入取件码:显示快递信息和柜子位置,从柜子中移除快递
任务过程
- 明确需求:练习 1、2 主要是实现用户登录,练习 1 为单用户的网络编程登录,练习 2 为多用户
- 先创建客户端和服务器端的编码,编码过程中,双方数据传递建议使用类对象
- 服务器端的多线程,本质就是让服务器一直处于运行状态,并且accept()请求后,分配新线程去处理请求
涉及知识点
思路及代码实现
下面的代码只涉及到多线程和网络编程的加入,实现用户登录和界面显示,因此代码不完整
一、 自定义异常
创建一个exception包,新建类OutNumberBoundException
OutNumberBoundException
public class OutNumberBoundException extends Throwable {
public OutNumberBoundException(String s) {
super(s);
}
}
二、视图分析
仅列出本文中涉及到的方法
创建一个view包,新建类ExpressView
- 进入系统
public static String welcome(){
return "欢迎进入快递管理系统!";
}
- 退出系统
public static String bye(){
return "感谢使用快递管理系统!";
}
- 身份选择
public static String mainMenu(){
return "请选择您的身份: 1.管理员 2.普通用户 0.退出";
}
- 输入账号和密码
/**
* 登录界面,输入账号
*/
public static String loginName(){
String s = "请输入登录用户名:";
return s;
}
/**
* 登录界面,输入密码
*/
public static String loginPassword(){
String s = "请输入登录密码:";
return s;
}
三、数据处理
仅列出本文中涉及到的方法
创建一个dao包,新建类ExpressDao
- 判断输入数字是否在有效范围内
public static int validNum(String s, int begin, int end) throws NumberFormatException, OutNumberBoundException {
try{
int num = Integer.parseInt(s);
if (num < begin || num > end){
throw new OutNumberBoundException("数字的范围必须在" + begin + "和" + end +"之间");
}
return num;
}catch(NumberFormatException | OutNumberBoundException e){
throw new NumberFormatException("输入的必须是数字!");
}
}
- 判断账号密码是否对应
public static boolean judgeLoginID(int n,String s1,String s2){//s1为用户名,s2为密码
if(n == 1){//管理员,用户名为admin,密码为abc
if("admin".equals(s1) && "abc".equals(s2)){//账号密码完全相同,即登陆成功
return true;
}
}else if(n == 2){//用户,用户名为user,密码为123
if("user".equals(s1) && "123".equals(s2)){//账号密码完全相同,即登陆成功
return true;
}
}
return false;
}
四、 C/S网络编程
- 主要负责服务器与客户端之间的交互
创建一个socket包,新建类Server和Client
Server服务器
在main中创建Server对象,并且调用start方法
public static void main(String[] args) throws IOException {
Server server = new Server();
server.start();
}
接下来编写start方法
1、 创建start方法,进行服务器的搭建运行以及客户端的连接
- 搭建服务器
ServerSocket server = new ServerSocket(55555);
- 进入服务器后等待客户端连接
使用while循环,实现用户的多次连接
System.out.println("服务器启动完毕,等待客户连接中...");
while(true){
//等待客户端连接
Socket socket = server.accept();
System.out.println("一个客户端连接了!");
}
- 加入多线程,使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
init(server,socket);//init方法来实现数据的交互
}
}).start();
start()方法完整代码如下:
public static void start() throws IOException {
//搭建服务器
ServerSocket server = new ServerSocket(55555);
//进入服务器
System.out.println("服务器启动完毕,等待客户连接中...");
while(true){
//等待客户端连接
Socket socket = server.accept();
System.out.println("一个客户端连接了!");
new Thread(new Runnable() {
@Override
public void run() {
init(server,socket);//init方法来实现数据的交互
}
}).start();
}
}
2、 编写任务run中的init方法实现与客户端数据的交互
- 收消息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
- 发消息
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
- 进入系统
send = v.welcome();
ps.println(send);//发消息,提示用户进入系统
ps.flush();//刷新流
- 发消息给用户需要输入的信息
//发消息,提示用户输入,1管理员,2普通用户,0退出
send = v.mainMenu();
ps.println(send);//发消息,提示用户输入菜单信息
- 接收用户的输入(管理员or用户)
do{
//收消息,1表示管理员,2表示普通用户,0表示退出
receive = br.readLine();
try{
mainNumber = dao.validNum(receive,0,2);
break;
}catch(NumberFormatException | OutNumberBoundException e){
ps.println("请输入0~2之间的有效数字: 1.管理员 2.普通用户 0.退出");
}
}while(true);
通过客户端输入的数字进入相应界面
- 0.退出
在服务器界面打印断开连接
System.out.println("客户端已断开与服务器的连接!");
- 1.管理员 或者 2.用户
(由于都是输入用户名和密码,因此输入部分一起处理) -
- 输入用户名和密码
//输入用户名
send= v.loginName();
ps.println(send);//请求客户端输入用户名,发送给客户端
name = br.readLine();//接收客户端输入的用户名
//输入密码
send = v.loginPassword();
ps.println(send);
password = br.readLine();
-
- 对用户名和密码进行判断是否正确
if (dao.judgeLoginID(1, name, password) || dao.judgeLoginID(2, name, password)) {
ps.println("登陆成功!");
ps.println(mainNumber);
}else{
ps.println("用户名或密码错误!登录失败!");
break m;
}
-
- 管理员
ps.println("尊敬的管理员,您好!");
ps.println("请选择您要进行的操作:");
ps.println("1.录入快递");
ps.println("2.删除快递");
ps.println("3.修改快递");
ps.println("4.查看所有快递");
ps.println("5.返回上一级界面");
-
- 用户
ps.println("尊敬的用户,您好!");
ps.println("请输入取件码:");
- 关闭流
input.close();
br.close();
ps.close();
socket.close();
server.close();
init方法的完整代码如下:
private static void init(ServerSocket server, Socket socket) {
try {
//接收客户消息,获取输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//发消息给客户,获取输出流
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
//与客户端交互
String receive,send;
//进入系统
send = v.welcome();
ps.println(send);
ps.flush();
//主菜单
//发消息,提示用户输入,1管理员,2普通用户,0退出
send = v.mainMenu();
ps.println(send);
m: while (true) {
int mainNumber;
String name,password;
do{
//收消息,1表示管理员,2表示普通用户,0表示退出
receive = br.readLine();
try{
mainNumber = dao.validNum(receive,0,2);
break;
}catch(NumberFormatException | OutNumberBoundException e){
ps.println("请输入0~2之间的有效数字: 1.管理员 2.普通用户 0.退出");
}
}while(true);
switch (mainNumber){
case 0://结束使用
System.out.println("客户端已断开与服务器的连接!");
break m;
default:{
//输入用户名
send= v.loginName();
ps.println(send);
name = br.readLine();
//输入密码
send = v.loginPassword();
ps.println(send);
password = br.readLine();
if (dao.judgeLoginID(1, name, password) || dao.judgeLoginID(2, name, password)) {
ps.println("登陆成功!");
ps.println(mainNumber);
}else{
ps.println("用户名或密码错误!登录失败!");
break m;
}
if(mainNumber == 1){
//managerPlatform();
ps.println("尊敬的管理员,您好!");
ps.println("请选择您要进行的操作:");
ps.println("1.录入快递");
ps.println("2.删除快递");
ps.println("3.修改快递");
ps.println("4.查看所有快递");
ps.println("5.返回上一级界面");
break m;//考虑到只题目要求用户登录成功提示登录成功并显示功能模块
//因此登陆成功并显示具体操作时程序便结束
}else if(mainNumber == 2){
// userPlatform();
ps.println("尊敬的用户,您好!");
ps.println("请输入取件码:");
break m;//考虑到只题目要求用户登录成功提示登录成功并显示功能模块
//因此登陆成功并显示具体操作时程序便结束
}
}
}//end switch
}//end while
input.close();
br.close();
ps.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Client客户端
- 连接到服务器
Socket socket = new Socket("127.0.0.1",55555);
- 发消息
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
- 收消息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
- 接收用户输入
BufferedReader input = new BufferedReader(newInputStreamReader(System.in));
- 进入系统提示
receive = br.readLine();
System.out.println(receive);
进入系统
- 发消息,选择对象(管理员or用户)以及输入用户名和密码
send = input.readLine();
ps.println(send);
- 如果输入0,则退出
if("0".equals(send)){
System.out.println(v.bye());
break;
}
- 如果登录成功,打印相应界面
int num = Integer.parseInt(br.readLine());
if(num == 1){
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
break;//考虑到只题目要求用户登录成功提示登录成功并显示功能模块
//因此登陆成功并显示具体操作时程序便结束
}else if(num == 2){
System.out.println(br.readLine());
System.out.println(br.readLine());
break;//考虑到只题目要求用户登录成功提示登录成功并显示功能模块
//因此登陆成功并显示具体操作时程序便结束
}
- 登录失败,退出系统
break;
- 退出系统,关闭流
input.close();
ps.close();
br.close();
socket.close();
客户端完整代码如下:
public static ExpressView v = new ExpressView();
//客户端
public static void main(String[] args) throws IOException {
// 连接到服务器
Socket socket = new Socket("127.0.0.1",55555);
//给服务器发消息
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
//接收服务器的消息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//读取键盘输入
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String receive,send;
//进入系统
receive = br.readLine();
System.out.println(receive);
//主菜单与登录界面
while(true){
//收消息,服务器发送的提示内容
receive = br.readLine();
System.out.println(receive);
if("登陆成功!".equals(receive) ){
int num = Integer.parseInt(br.readLine());
if(num == 1){
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
break;//考虑到只题目要求用户登录成功提示登录成功并显示功能模块
//因此登陆成功并显示具体操作时程序便结束
}else if(num == 2){
System.out.println(br.readLine());
System.out.println(br.readLine());
break;//考虑到只题目要求用户登录成功提示登录成功并显示功能模块
//因此登陆成功并显示具体操作时程序便结束
}
}else if("用户名或密码错误!登录失败!".equals(receive)){
break;
}
//发消息
send = input.readLine();
ps.println(send);
if("0".equals(send)){
System.out.println(v.bye());
break;
}
}//end while 聊天结束
input.close();
ps.close();
br.close();
socket.close();
}