网络多线程开发小项目--QQ登陆聊天功能(用户登陆功能实现)

9.1.1用户登陆功能实现

在这里插入图片描述
1、需求分析
在这里插入图片描述
2、代码实现
2.1、Client和Server端共有类
1)cn.com.agree.qqcommon.Message

package cn.com.agree.qqcommon;import lombok.Data;
import lombok.extern.slf4j.Slf4j;import java.io.Serializable;
@Slf4j
@Data
public class Message implements Serializable {public static final long serialVersionUID = 1L;private String sender;//发送者private String getter;//接受者private String content;//消息内容private String sendTime;//发送时间private String msgType;//消息类型(可以在接口定义消息类型)}

2)cn.com.agree.qqcommon.User

package cn.com.agree.qqcommon;import lombok.Data;
import lombok.extern.slf4j.Slf4j;import java.io.Serializable;
@Slf4j
@Data
public class User implements Serializable {public static final long serialVersionUID = 1L;private String userId;//用户id代表一个用户private String passwd;//登陆密码
}

3)cn.com.agree.qqcommon.MessageType

package cn.com.agree.qqcommon;public interface MessageType {//消息类型//1、在消息类型中定义一些常量//2、不同的常量值代表不同的消息类型String MESSAGE_LOG_SUCCESS = "1";//表示登陆成功String MESSAGE_LOG_FAIL = "2";//表示登陆失败
}

2.2 QQClient端:
1)界面QQView

package cn.com.agree.qqclient.QQView;import cn.com.agree.qqclient.service.UserClientService;
import cn.com.agree.qqcommon.User;
import cn.com.agree.util.Utility;import lombok.extern.slf4j.Slf4j;@Slf4j
public class QQView {public static void main(String[] args) {new QQView().mainMenu();log.debug("退出系统!!!");}private final UserClientService userClientService = new UserClientService();private void mainMenu() {String key = "";boolean loop = true;//控制程序是显示主菜单while (loop) {System.out.println("==========欢迎登陆网络通讯系统============");System.out.println("======\t\t 1 登陆系统\t\t==========");System.out.println("======\t\t 9 退出系统\t\t==========");log.debug("请输入你的选择:");key = Utility.readString(1);switch (key) {case "1":log.debug("请输入用户名:");String userId = Utility.readString(300);log.debug("请输入密  码:");String passwd = Utility.readString(1234);//todo 此处去服务端进行用户名密码校验User user = userClientService.checkUser(userId, passwd);if (user != null) {System.out.println("==========欢迎" + userId + "用户=============");while (loop) {System.out.println("==========网络通讯系统二级菜单(用户 " + userId + ")============");System.out.println("=======\t\t 1 显示在线用户列表================");System.out.println("=======\t\t 2 群发消息\t\t================");System.out.println("=======\t\t 3 私聊消息\t\t================");System.out.println("=======\t\t 4 发送文件\t\t================");System.out.println("=======\t\t 9 退出系统\t\t================");log.debug("请输入你的选择:");key = Utility.readString(1);switch (key) {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":loop = false;break;default:log.debug("输入有误,请重新输入!");break;}}} else {log.debug("=========用户名密码错误=============");log.debug("==========登陆失败=============");}break;case "9":loop = false;break;default:log.debug("输入有误,请重新输入!");break;}}}
}

2)工具类cn.com.agree.util.Utility

package cn.com.agree.util;import lombok.extern.slf4j.Slf4j;import java.util.Scanner;/*** @author andongdong* @version 1.0* @ClassName Utility* @Description 工具类* @date 2024/1/5  10:56 上午**/
@Slf4j
public class Utility {public static Scanner scanner;static {scanner = new Scanner(System.in);}public Utility() {}public static char readMenuSelection() {while (true) {String str = readKeyBoard(1, false);char c = str.charAt(0);if (c == '1' || c == '2' || c == '3' || c == '4' || c == '5') {return c;}log.debug("选择错误,请重新输入:");}}public static char readChar() {String str = readKeyBoard(1, false);return str.charAt(0);}public static char readChar(char defaultValue) {String str = readKeyBoard(1, true);return str.length() == 0 ? defaultValue : str.charAt(0);}public static int readInt() {while (true) {String str = readKeyBoard(2, false);try {int n = Integer.parseInt(str);return n;} catch (NumberFormatException var3) {log.debug("数字输入错误,请重新输入:");}}}public static int readInt(int defaultValue) {while (true) {String str = readKeyBoard(2, true);if (str.equals("")) {return defaultValue;}try {int n = Integer.parseInt(str);return n;} catch (NumberFormatException var3) {log.debug("数字输入错误,请重新输入:");}}}public static String readString(int limit) {return readKeyBoard(limit, false);}public static String readString(int limit, String defaultValue) {String str = readKeyBoard(limit, true);return str.equals("") ? defaultValue : str;}public static char readConfirmSelection() {while (true) {String str = readKeyBoard(1, false).toUpperCase();char c = str.charAt(0);if (c == 'Y' || c == 'N') {return c;}log.debug("选择错误,请重新输入: ");}}private static String readKeyBoard(int limit, boolean blankReturn) {String line = "";while (scanner.hasNextLine()) {line = scanner.nextLine();if (line.length() == 0) {if (blankReturn) {return line;}} else {if (line.length() >= 1 && line.length() <= limit) {break;}log.debug("输入长度(不大于" + limit + ")" + "错误,请重新输入:");}}return line;}
}

3)cn.com.agree.qqclient.service.UserClientService

package cn.com.agree.util;import lombok.extern.slf4j.Slf4j;import java.util.Scanner;
/*** @author * @version 1.0* @ClassName UserClientService* @Description TODO 类描述* @date 2024/1/5  4:18 下午**/
@Slf4j
public class Utility {public static Scanner scanner;static {scanner = new Scanner(System.in);}public Utility() {}public static char readMenuSelection() {while (true) {String str = readKeyBoard(1, false);char c = str.charAt(0);if (c == '1' || c == '2' || c == '3' || c == '4' || c == '5') {return c;}log.debug("选择错误,请重新输入:");}}public static char readChar() {String str = readKeyBoard(1, false);return str.charAt(0);}public static char readChar(char defaultValue) {String str = readKeyBoard(1, true);return str.length() == 0 ? defaultValue : str.charAt(0);}public static int readInt() {while (true) {String str = readKeyBoard(2, false);try {int n = Integer.parseInt(str);return n;} catch (NumberFormatException var3) {log.debug("数字输入错误,请重新输入:");}}}public static int readInt(int defaultValue) {while (true) {String str = readKeyBoard(2, true);if (str.equals("")) {return defaultValue;}try {int n = Integer.parseInt(str);return n;} catch (NumberFormatException var3) {log.debug("数字输入错误,请重新输入:");}}}public static String readString(int limit) {return readKeyBoard(limit, false);}public static String readString(int limit, String defaultValue) {String str = readKeyBoard(limit, true);return str.equals("") ? defaultValue : str;}public static char readConfirmSelection() {while (true) {String str = readKeyBoard(1, false).toUpperCase();char c = str.charAt(0);if (c == 'Y' || c == 'N') {return c;}log.debug("选择错误,请重新输入: ");}}private static String readKeyBoard(int limit, boolean blankReturn) {String line = "";while (scanner.hasNextLine()) {line = scanner.nextLine();if (line.length() == 0) {if (blankReturn) {return line;}} else {if (line.length() >= 1 && line.length() <= limit) {break;}log.debug("输入长度(不大于" + limit + ")" + "错误,请重新输入:");}}return line;}
}

4)cn.com.agree.qqclient.service.ClientConnectServer

package cn.com.agree.qqclient.service;import cn.com.agree.qqcommon.Message;
import lombok.extern.slf4j.Slf4j;import java.io.ObjectInputStream;
import java.net.Socket;
/*** @author * @version 1.0* @ClassName ClientConnectServer* @Description 为了便于管理,将客户端的thread根据UserID存入到一个集合中* @date 2024/1/5  3:54 下午**/
@Slf4j
public class ClientConnectServer implements Runnable {//该线程需要持有socketprivate Socket socket;public Socket getSocket() {return socket;}public void setSocket(Socket socket) {this.socket = socket;}public ClientConnectServer(Socket socket) {this.socket = socket;}@Overridepublic void run() {//因为Thread需要在后台和服务器进行通信,因此我们用while循环while (true) {try {log.debug("客户端线程和服务端正在通信,读取从服务端发来的消息...");// 设置Socket的超时时间为5000毫秒(5秒)
//                socket.setSoTimeout(5000);ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());Message message = (Message) ois.readObject();//如果服务器端没有消息发过来这个线程一直会阻塞在这里//todo 后续对读取到的message进行处理log.debug("message:{}",message);} catch (Exception e) {e.printStackTrace();}}}
}

5)cn.com.agree.qqclient.service.ManageClientConnectServers

package cn.com.agree.qqclient.service;import cn.com.agree.qqcommon.Message;
import lombok.extern.slf4j.Slf4j;import java.io.ObjectInputStream;
import java.net.Socket;/*** @author * @version 1.0* @ClassName ClientConnectServer* @Description 为了便于管理,将客户端的thread根据UserID存入到一个集合中* @date 2024/1/5  3:54 下午**/
@Slf4j
public class ClientConnectServer implements Runnable {//该线程需要持有socketprivate Socket socket;public Socket getSocket() {return socket;}public void setSocket(Socket socket) {this.socket = socket;}public ClientConnectServer(Socket socket) {this.socket = socket;}@Overridepublic void run() {//因为Thread需要在后台和服务器进行通信,因此我们用while循环while (true) {try {log.debug("客户端线程和服务端正在通信,读取从服务端发来的消息...");// 设置Socket的超时时间为5000毫秒(5秒)
//                socket.setSoTimeout(5000);ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());Message message = (Message) ois.readObject();//如果服务器端没有消息发过来这个线程一直会阻塞在这里//todo 后续对读取到的message进行处理log.debug("message:{}",message);} catch (Exception e) {e.printStackTrace();}}}
}

2.3、QQServer端:
1)cn.com.agree.qqserver.service.qqServer

package cn.com.agree.qqserver.service;import cn.com.agree.qqcommon.Message;
import cn.com.agree.qqcommon.MessageType;
import cn.com.agree.qqcommon.User;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;/*** @author * @version 1.0* @ClassName qqServer* @Description 服务器端编码* @date 2024/1/5  5:24 下午**/
@Slf4j
public class qqServer {/** 用hashmap模拟数据库*/private static ConcurrentHashMap<String,User> validUsers = new ConcurrentHashMap<>();static {validUsers.put("100",new User("100","123456"));validUsers.put("200",new User("200","123456"));validUsers.put("300",new User("300","123456"));validUsers.put("张三",new User("张三","123456"));validUsers.put("李四",new User("李四","123456"));validUsers.put("王五",new User("王五","123456"));}private ServerSocket serverSocket = null;public qqServer() {Socket socket = null;try {log.debug("服务端在9999端口监听,等待客户端连接...");serverSocket = new ServerSocket(9999);//因为服务端会监听来自不同线程到socket,所以它要保持一直在监听到状态不能只监听到一个就退出因此用循环while (true) {socket = serverSocket.accept();//获取来自客户端发送过来到用户对象log.debug("socket Client连接成功");ObjectInputStream bis = new ObjectInputStream(socket.getInputStream());User user = (User) bis.readObject();//获取输出流对象写入返回给客户端的消息ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());log.debug("user:{}",user);Message message = new Message();//TODO 校验用户信息(数据库层面校验这里先写死判断后面改进)if (checkUser(user.getUserId(), user.getPasswd())) {//校验成功message.setMsgType(MessageType.MESSAGE_LOG_SUCCESS);oos.writeObject(message);//创建一个server连接client的线程去完成通信->ServerConnectClientServerConnectClient serverConnectClient = new ServerConnectClient(socket, user.getUserId());new Thread(serverConnectClient).start();//使用集合类管理服务端线程ManageServerConnectClient.addServerConnectClient(user.getUserId(), serverConnectClient);} else {//校验失败log.debug("userId为"+user.getUserId()+"登陆失败");message.setMsgType(MessageType.MESSAGE_LOG_FAIL);oos.writeObject(message);socket.close();}}} catch (Exception e) {e.getStackTrace();} finally {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}private boolean checkUser(String userId,String passwd){User user = validUsers.get(userId);if(user == null){//用户不存在return false;}if(!passwd.equals(user.getPasswd())){//密码错误return false;}return true;}
}

2)cn.com.agree.qqserver.service.ServerConnectClient

package cn.com.agree.qqserver.service;import cn.com.agree.qqcommon.Message;
import cn.com.agree.qqcommon.User;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;/*** @author * @version 1.0* @ClassName ServerConnectClient* @Description TODO 类描述* @date 2024/1/5  5:44 下午**/
@Slf4j
public class ServerConnectClient implements  Runnable {private Socket socket;private String userId;public ServerConnectClient(Socket socket, String userId) {this.socket = socket;this.userId = userId;}public void run() {while (true){try {log.debug("服务端和客户端"+userId+"正在通信通信,读取数据...");// 设置Socket的超时时间为5000毫秒(5秒)
//                socket.setSoTimeout(5000);ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());Message message = (Message) ois.readObject();//TODO 后续处理从客户端获取的消息log.debug("message:{}",message);} catch (Exception e) {e.printStackTrace();}}}
}

3)cn.com.agree.qqclient.service.ClientConnectServer

package cn.com.agree.qqserver.service;import java.util.HashMap;
import java.util.Map;/*** @author * @version 1.0* @ClassName ManageServerConnectClient* @Description TODO 类描述* @date 2024/1/5  5:50 下午**/
public class ManageServerConnectClient {private static Map<String,ServerConnectClient> hm = new HashMap();public static void addServerConnectClient(String userId,ServerConnectClient serverConnectClient){hm.put(userId,serverConnectClient);}public static ServerConnectClient getServerConnectClient(String userId) {return hm.get(userId);}
}

4)启动类

package cn.com.agree.qqserver.service;  /*** @author * @version 1.0* @ClassName qqFrame* @Description TODO 类描述* @date 2024/1/5  6:06 下午**/
public class qqFrame {public static void main(String[] args) {new qqServer();}
}

3、运行效果
1)校验错误的用户名密码
在这里插入图片描述
2)校验正确的用户名密码(起两个客户端一个服务端)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/333861.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pve多台物理机虚拟化 pve虚拟机优势

Proxmox VE是一个运行虚拟机和容器的平台。基于Debian Linux&#xff0c;完全开源。为了获得最大的灵活性&#xff0c;实现了两种虚拟化技术——基于内核的虚拟机(KVM)和基于容器的虚拟化(LXC)。一个主要的设计目标是使管理尽可能容易。运行在单个节点上使用Proxmox VE&#xf…

基于SSM的校园二手交易管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

AIGC无人直播系统技术

随着信息技术的快速发展和互联网的普及&#xff0c;直播行业迎来了蓬勃发展的机遇。然而&#xff0c;传统的直播方式存在一些局限性&#xff0c;如场地限制、设备携带不便等问题。为了解决这些问题&#xff0c;AIGC推出了一项创新性的无人直播系统技术。 AIGC无人直播系统技术…

linux 系统安全及应用

一、账号安全基本措施 1.系统账号清理 1.将用户设置为无法登录 /sbin/nologin shell——/sbin/nologin却比较特殊&#xff0c;所谓“无法登陆”指的仅是这个用户无法使用bash或其他shell来登陆系统而已&#xff0c;并不是说这个账号就无法使用系统资源。举例来说&#xff0c;…

GIT - 清除历史 Commit 瘦身仓库

目录 一.引言 二.仓库清理 ◆ 创建一个船新分支 ◆ 提交最新代码 ◆ 双指针替换分支 三.总结 一.引言 由于项目运行时间较长&#xff0c;分支较多&#xff0c;且分支内包含很多不同的大文件&#xff0c;随着时间的推移&#xff0c;历史 Git 库的容量日渐增发&#xff0c…

密码学(三)

文章目录 前言一、Software Attestation Overview二、Authenticated Key Agreement三、The Role of Software Measurement 前言 本文来自 Intel SGX Explained 请参考&#xff1a; 密码学&#xff08;一&#xff09; 密码学&#xff08;二&#xff09; 一、Software Attesta…

SG-8018CA 系列 (晶体振荡器 可编程 可用+105°C )

SG-8018系列是可编程晶体振荡器系列与CMOS输出。虽然该系列提供了与早期SG-8002/SG-8003系列相同的易于编程的频率和其他参数的相似性&#xff0c;但它们也有一个更广泛的工作温度范围&#xff0c;最高限制为105C。除了2.52.0 mm封装&#xff0c;将使电子产品制造商节省板空间&…

usb转32串口方案

方案结构图 使用usb hub芯片扩展4路usb然后再一分八路串口 USB hub 选择hub芯片注意事项&#xff1a; 目前市场上多数的USB 2.0 Hub芯片,只有内建一个Transaction Translators(STT)&#xff0c;因此 当Hub接收到如Full Speed的装置进入时&#xff0c;12Mbps的「单一」信道…

Java字符串拼接常用方法总结

使用场景&#xff1a;用某个分隔符拼接字符串 下边是我使用过的几种方式废话不多说&#xff0c;直接上代码初始数据 1.使用流2.StringBuilder3.[StringJoiner](https://blog.csdn.net/qq_43417581/article/details/126076152?ops_request_misc%257B%2522request%255Fid%2522%2…

如何使用TrafficWatch根据PCAP文件监控和分析网络流量

关于TrafficWatch TrafficWatch是一款功能强大的网络数据包嗅探和分析工具&#xff0c;该工具能够帮助我们通过PCAP文件监控和分析目标网络中的网络通信流量。除此之外&#xff0c;该工具还可以为广大研究人员提供针对各种网络协议的内部分析功能&#xff0c;并帮助解决各种网…

hadoop自动获取时间

1、自动获取前15分钟 substr(from_unixtime(unix_timestamp(concat(substr(20240107100000,1,4),-,substr(20240107100000,5,2),-,substr(20240107100000,7,2), ,substr(20240107100000,9,2),:,substr(20240107100000,11,2),:,00))-15*60,yyyyMMddHHmmss),1) unix_timestam…

Win提示“d3dx9_27.dll文件缺失,程序无法启动运行”,修复大全

d3dx9_27.dll是一个被多个软件和游戏共享的动态链接库文件&#xff0c;主要用于Microsoft DirectX软件的功能。它是DirectX 9的一部分&#xff0c;DirectX是一种使得Windows成为理想平台进行高性能多媒体和游戏的API。 d3dx9_27.dll主要与计算机图形和视频渲染有关&#xff0c…