Java-多用户即时通信系统-用户登录(客户端)
1 实体类(放在common包下)
- 包括用户类(User)、消息类(Message)、消息类型接口(MessageType)
User
/**
* @author: SEA
* @date: 2023/5/4
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;//序列化ID,用来保证序列化和反序列化过程中,User类的一致性
private String userID;//用户名
private String password;//密码
public User() {
}
public User(String userID, String password) {
this.userID = userID;
this.password = password;
}
public String getuserID() {
return userID;
}
public void setuserID(String userID) {
this.userID = userID;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"userID='" + userID + '\'' +
", password='" + password + '\'' +
'}';
}
}
Message
/**
* @author: SEA
* @date: 2023/5/4
*/
public class Message implements Serializable {
private static final long serialVersionUID = 1L;//序列化ID,用来保证序列化和反序列化过程中,User类的一致性
private String sender_ip;//发送方ip
private String receiver_ip;//接收方ip
private String send_data;//发送内容
private String send_time;//发送时间
private String message_type;//消息类型(如:文本,图片,音频,视频等)
public Message() {
}
public Message(String sender_ip, String receiver_ip, String send_data, String send_time, String message_type) {
this.sender_ip = sender_ip;
this.receiver_ip = receiver_ip;
this.send_data = send_data;
this.send_time = send_time;
this.message_type = message_type;
}
public String getSender_ip() {
return sender_ip;
}
public void setSender_ip(String sender_ip) {
this.sender_ip = sender_ip;
}
public String getReceiver_ip() {
return receiver_ip;
}
public void setReceiver_ip(String receiver_ip) {
this.receiver_ip = receiver_ip;
}
public String getSend_data() {
return send_data;
}
public void setSend_data(String send_data) {
this.send_data = send_data;
}
public String getSend_time() {
return send_time;
}
public void setSend_time(String send_time) {
this.send_time = send_time;
}
public String getMessage_type() {
return message_type;
}
public void setMessage_type(String message_type) {
this.message_type = message_type;
}
}
MessageType
/**
* @author: sea-365
* @date: 2023/5/4 20:54
*/
public interface MessageType {
String MESSAGE_LOGIN_SUCCEED = "1";//表示登录成功
String MESSAGE_LOGIN_FAILED = "1";//表示登录成功
}
2 功能类(放在service包下)
UserClientService
/**
* 该类完成用户登录验证,用户注册等功能
* @author: SEA
* @date: 2023/5/5
*/
public class UserClientService {
User user = new User();//User对象
Socket socket = null;//Socket对象
/**
* 用户登录验证
* @param userID 用户名
* @param password 密码
* @return 返回登录验证结果
*/
public boolean checkUser(String userID, String password) throws IOException, ClassNotFoundException {
user.setuserID(userID);
user.setPassword(password);
//向服务器发送User对象,根据服务器返回的结果进行验证
socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
//发送user对象
objectOutputStream.writeObject(user);
//socket.shutdownOutput();
//读取服务器端返回的验证结果
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
//读取返回的Message对象
Message message = (Message) objectInputStream.readObject();
if(message.getMessage_type().equals(MessageType.MESSAGE_LOGIN_SUCCEED)) {//登录成功
//登录成功,创建一个与服务器端保持通信的线程 -> 创建一个线程类ClientConnectServerThread
ClientConnectServerThread clientConnectServerThread = new ClientConnectServerThread();
//启动客户端与服务器端通信的线程
clientConnectServerThread.start();
//为了方便拓展,将该线程放在一个集合中进行管理
//【关于“拓展”】:比如,客户端在私聊另一个客户端的同时,还在与其他客户端发送文件,此时需要同时进行两个任务,使用多线程并发实现
ClientConnectServerThreadManage.addClientConnectServerThread(userID, clientConnectServerThread);
return true;
}
else {//登录失败
//应该关闭打开的流 和 Socket对象
objectInputStream.close();
socket.close();
return false;
}
}
}
ClientConnectServerThread
/**
* 客户端保持与服务器端通信的线程
* @author: SEA
* @date: 2023/5/5
*/
public class ClientConnectServerThread extends Thread{
//持有用于网络通信的Socket对象
private Socket socket;
public ClientConnectServerThread(){ }
public ClientConnectServerThread(Socket socket){
this.socket = socket;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//客户端需要在后台一直保持与服务器端通信,因此使用while循环;
//【因此这种阻塞式IO的方式,导致效率比较低!】
while(true){
try {
ObjectInputStream objectInputStream = new ObjectInputStream(this.getSocket().getInputStream());
//读取服务器端发送的数据,如果没有发送数据,则线程阻塞!
Message message = (Message) objectInputStream.readObject();
System.out.println(" 处理服务器端发送的消息: " +message.getSend_data());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
ClientConnectServerThreadManage
- 管理ClientConnectServerThread线程的类
/**
* @author: SEA
* @date: 2023/5/5
*/
public class ClientConnectServerThreadManage {
//管理ClientConnectServerThread线程类的集合, 以userID为key;
private static HashMap<String, ClientConnectServerThread> clientConnectServerThreads = new HashMap<>();
public ClientConnectServerThreadManage() {
}
//向集合添加一个线程
public static void addClientConnectServerThread(String userID, ClientConnectServerThread clientConnectServerThread){
clientConnectServerThreads.put(userID, clientConnectServerThread);
}
//根据userID获取集合中的某个线程
public static ClientConnectServerThread getClientConnectServerThread(String userID){
return clientConnectServerThreads.get(userID);
}
}
3 工具类(放在utils包下)
Util_KeyBoardInput
/**
* @author: SEA
* @date: 2023/5/5
*/
public class Util_KeyBoardInput {
//静态属性scanner
private static Scanner scanner = new Scanner(System.in);
/**
* 读取键盘输入的指定长度的字符串(不能为空串,limit >= 1)
* @param limit 限定的长度
* @return 指定长度的字符串
*/
public static String readString(int limit){
return readKeyBoard(limit, false);
}
/**
* 功能: 读取一个字符串
* @param limit 读取的长度
* @param blankReturn 如果为true ,表示 可以读空字符串。
* 如果为false表示 不能读空字符串。
* 如果输入为空,或者输入大于limit的长度,就会提示重新输入。
* @return
*/
private static String readKeyBoard(int limit, boolean blankReturn) {
String line = "";
while(scanner.hasNextLine()){
line = scanner.nextLine();
//如果line.length=0, 即用户没有输入任何内容,直接回车
if (line.length() == 0) {
if (blankReturn) return line;//如果blankReturn=true,可以返回空串
else continue; //如果blankReturn=false,不接受空串,必须输入内容
}
//如果用户输入的内容大于了 limit,就提示重写输入
//如果用户如的内容 >0 <= limit ,我就接受
if (line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不能大于" + limit + ")错误,请重新输入:");
continue;
}
break;
}
return line;
}
}
4 界面类(放在view包下)
QQClientView
- 客户端界面类(目前没有前端,使用命令行输出的方式展示前端);
/**
* @author: SEA
* @date: 2023/5/5
*/
public class QQClientView {
private boolean isLoop = true;//是否退出系统
private String userID, password;//用户输入的登录信息
private UserClientService userClientService = new UserClientService();//用于用户登录注册等功能的对象
public void mainView() throws Exception {
String selectItem;
Scanner scanner = new Scanner(System.in);
while(isLoop){
System.out.println("=============== 欢迎登录网络通信系统 ===============");
System.out.println("\t\t\t\t 1 登录系统 \t\t");
System.out.println("\t\t\t\t 9 退出系统 \t\t");
System.out.print("请输入您的选择:");
selectItem = Util_KeyBoardInput.readString(1);
switch (selectItem){
case "1":
System.out.print("请输入用户名: ");
userID = Util_KeyBoardInput.readString(50);
System.out.print("请输入密 码:");
password = Util_KeyBoardInput.readString(50);
System.out.println("登录验证中......");
if(userClientService.checkUser(userID, password)){
System.out.println("=============== 欢迎您("+ userID +")登录网络通信系统 ===============");
//进入二级菜单
while(isLoop){
System.out.println("=============== 欢迎您("+ userID +"),这里是网络通信系统二级菜单 ===============");
System.out.println("\t\t\t\t 1 显示在线用户列表 \t\t");
System.out.println("\t\t\t\t 2 群发消息 \t\t");
System.out.println("\t\t\t\t 3 私聊消息 \t\t");
System.out.println("\t\t\t\t 4 发送文件 \t\t");
System.out.println("\t\t\t\t 9 退出系统 \t\t");
System.out.print("请输入您的选择:");
selectItem = Util_KeyBoardInput.readString(1);
switch (selectItem){
case "1":
System.out.println("正在显示在线用户列表......");
break;
case "2":
System.out.println("群发消息中......");
break;
case "3":
System.out.println("私聊消息中......");
break;
case "4":
System.out.println("发送文件中......");
break;
case "9":
isLoop = false;
break;
default:
System.out.println("请输入正确的选项!");
break;
}
}
}
else{
System.out.println("登录失败!请检查用户名和密码是否正确。");
}
break;
case "9":
isLoop = false;
break;
default:
System.out.println("请输入正确的选项!");
break;
}
}
}
public static void main(String[] args) {
QQClientView qqView = new QQClientView();
try {
qqView.mainView();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}