Java初学笔记30

  • [一] 项目开发流程的简介
  • [二] 多用户及时通讯系统
    • 1. 涉及到知识点
    • 2. 需求分析
    • 3. 整体框架
  • 一、commen
    • Message类
    • User类
    • MessageType接口
    • Utility 工具类
  • 二、Server
    • ManageClientThread
    • QQServer
    • SendNewsToAllClient
    • SeverConnectClientThread
    • qqFrame
  • 三、Client
    • ClinetConnectServerThread
    • FileClientService
    • ManageClinetConnectServerThread
    • MessageClientService
    • UserClientService
    • qqView

[一] 项目开发流程的简介

(1)需求分析
一般由需求分析师来完成,最后写出一个需求分析报告,其中包括项目的功能,客户的需求
(2)设计阶段
由架构师或者项目经理完成,设计出UML类图、流程图、模块设计,数据库设计,以及原型开发;组建团队。
(3)实现阶段
程序员,完成架构师的模块功能,测试自己的模块
(4)测试阶段
由测试工程师完成,单元测试,白盒测试,黑盒测试,集合测试,找出程序员写的代码bug
(5)实施阶段
由实施工程师完成,把项目部署到客户平台,并保障运行正常。
(6)维护阶段
发现Bug解决,升级

[二] 多用户及时通讯系统

1. 涉及到知识点

项目框架设计、java面向对象编程、网络编程、多线程、IO流、 Mysql(可以先使用集合充当数据库)

2. 需求分析

(1)用户登录
(2)拉取在线用户列表
(3)无异常退出
(4)私聊
(5)群聊
(6)发文件
(7)服务器推送新闻

3. 整体框架

一、commen

Message类

package commen;import java.io.Serializable;/*** @Package: qqCommen* @ClassName: Message* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/15 17:42* @Description:  消息对象*/
public class Message implements Serializable {private static final long serialVersionUID = 1l; //串行化好兼容private String sender; //发送者private String getter; //接收者private String content; //消息内容private String sendTime; //发送时间private String mesType; //消息类型private byte[] fileBytes;private int fileLen = 0;private String dest; //目标文件路径private String src; //源头文件路径public byte[] getFileBytes() {return fileBytes;}public void setFileBytes(byte[] fileBytes) {this.fileBytes = fileBytes;}public int getFileLen() {return fileLen;}public void setFileLen(int fileLen) {this.fileLen = fileLen;}public String getDest() {return dest;}public void setDest(String dest) {this.dest = dest;}public String getSrc() {return src;}public void setSrc(String src) {this.src = src;}/*** 构造器* @param sender* @param getter* @param content* @param sendTime* @param mesType*/public Message(String sender, String getter,String content, String sendTime, String mesType) {this.sender = sender;this.getter = getter;this.content = content;this.sendTime = sendTime;this.mesType = mesType;}public Message() {}public String getMesType() {return mesType;}public void setMesType(String mesType) {this.mesType = mesType;}public String getSender() {return sender;}public void setSender(String sender) {this.sender = sender;}public String getGetter() {return getter;}public void setGetter(String getter) {this.getter = getter;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getSendTime() {return sendTime;}public void setSendTime(String sendTime) {this.sendTime = sendTime;}
}

User类

package commen;import java.io.Serializable;/*** @Package: qqCommen* @ClassName: User* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/15 17:41* @Description: 用户对象* 通过对象流传输*/
public class User implements Serializable {/*** 串行化好兼容*/private static final long serialVersionUID = 1l;/*** 用户ID*/private String userID;/*** 用户密码*/private String passwd;/*** 以下为构造器*/public User() {}public User(String userID, String passwd) {this.userID = userID;this.passwd = passwd;}/*** get set方法* @return*/public String getUserID() {return userID;}public void setUserID(String userID) {this.userID = userID;}public String getPasswd() {return passwd;}public void setPasswd(String passwd) {this.passwd = passwd;}
}

MessageType接口

package commen;/*** @Package: qqCommen* @ClassName: MessageType* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/15 17:48* @Description:*/
public interface MessageType {//登录成功String MESSAGE_LOGIN_SUCCEED = "1";//登录失败String MESSAGE_LOGIN_FAIL = "2";//普通信息包String MESSAGE_COMMON_MESSAGE = "3";//要求返回在线用户列表String MESSAGE_GET_ONLINE_FRIEND = "4";//返回在线用户列表String MESSAGE_RETURN_ONLINE_FRIEND = "5";//客户端请求推出String MESSAGE_CLIENT_EXIT = "6";//群发信息包String MESSAGE_TO_ALL_MESSAGE = "7";//发送文件数据String MESSAGE_FILE_MESSAGE = "8";//离线消息String MESSAGE_UnLine_MESSAGE = "9";
}

Utility 工具类

package utility;/**工具类的作用:处理各种情况的用户输入,并且能够按照程序员的需求,得到用户的控制台输入。
*/import java.util.Scanner;/***/
public class Utility {//静态属性。。。private static Scanner scanner = new Scanner(System.in);/*** 功能:读取键盘输入的一个菜单选项,值:1——5的范围* @return 1——5*/public static char readMenuSelection() {char c;for (; ; ) {String str = readKeyBoard(1, false);//包含一个字符的字符串c = str.charAt(0);//将字符串转换成字符char类型if (c != '1' && c != '2' && c != '3' && c != '4' && c != '5') {System.out.print("选择错误,请重新输入:");} else break;}return c;}/*** 功能:读取键盘输入的一个字符* @return 一个字符*/public static char readChar() {String str = readKeyBoard(1, false);//就是一个字符return str.charAt(0);}/*** 功能:读取键盘输入的一个字符,如果直接按回车,则返回指定的默认值;否则返回输入的那个字符* @param defaultValue 指定的默认值* @return 默认值或输入的字符*/public static char readChar(char defaultValue) {String str = readKeyBoard(1, true);//要么是空字符串,要么是一个字符return (str.length() == 0) ? defaultValue : str.charAt(0);}/*** 功能:读取键盘输入的整型,长度小于2位* @return 整数*/public static int readInt() {int n;for (; ; ) {String str = readKeyBoard(2, false);//一个整数,长度<=2位try {n = Integer.parseInt(str);//将字符串转换成整数break;} catch (NumberFormatException e) {System.out.print("数字输入错误,请重新输入:");}}return n;}/*** 功能:读取键盘输入的 整数或默认值,如果直接回车,则返回默认值,否则返回输入的整数* @param defaultValue 指定的默认值* @return 整数或默认值*/public static int readInt(int defaultValue) {int n;for (; ; ) {String str = readKeyBoard(10, true);if (str.equals("")) {return defaultValue;}//异常处理...try {n = Integer.parseInt(str);break;} catch (NumberFormatException e) {System.out.print("数字输入错误,请重新输入:");}}return n;}/*** 功能:读取键盘输入的指定长度的字符串* @param limit 限制的长度* @return 指定长度的字符串*/public static String readString(int limit) {return readKeyBoard(limit, false);}/*** 功能:读取键盘输入的指定长度的字符串或默认值,如果直接回车,返回默认值,否则返回字符串* @param limit 限制的长度* @param defaultValue 指定的默认值* @return 指定长度的字符串*/public static String readString(int limit, String defaultValue) {String str = readKeyBoard(limit, true);return str.equals("")? defaultValue : str;}/*** 功能:读取键盘输入的确认选项,Y或N* 将小的功能,封装到一个方法中.* @return Y或N*/public static char readConfirmSelection() {System.out.println("请输入你的选择(Y/N)");char c;for (; ; ) {//无限循环//在这里,将接受到字符,转成了大写字母//y => Y n=>NString str = readKeyBoard(1, false).toUpperCase();c = str.charAt(0);if (c == 'Y' || c == 'N') {break;} else {System.out.print("选择错误,请重新输入:");}}return c;}/*** 功能: 读取一个字符串* @param limit 读取的长度* @param blankReturn 如果为true ,表示 可以读空字符串。 *                    如果为false表示 不能读空字符串。*          *   如果输入为空,或者输入大于limit的长度,就会提示重新输入。* @return*/private static String readKeyBoard(int limit, boolean blankReturn) {//定义了字符串String line = "";//scanner.hasNextLine() 判断有没有下一行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;}
}

二、Server

ManageClientThread

package Service;import commen.Message;import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;/*** @Package: Service* @ClassName: ManageClientThread* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/17 18:42* @Description: 服务器中 管理线程的集合 类*/
public class ManageClientThread {//用hashmap存储服务器连接到客户端的线程private static HashMap<String,SeverConnectClientThread> hm = new HashMap<>();//用ConcurrentHashMap存储离线的线程的messageprivate static ConcurrentHashMap<String, ArrayList<Message>> chm= new ConcurrentHashMap<>();/*** 返回ConcurrentHashMap* @return*/public static ConcurrentHashMap<String, ArrayList<Message>> getChm() {return chm;}/*** 返回hashmap* @return*/public static HashMap<String, SeverConnectClientThread> getHm() {return hm;}/*** 添加线程到服务器的Hashmap中* @param userID* @param severConnectClientThread*/public static void addClientThread(String userID,SeverConnectClientThread severConnectClientThread){hm.put(userID, severConnectClientThread);}/*** 添加离线线程的message到服务器的Hashmap中的arraylist中* @param userID  离线的线程ID* @param message 离线的线程的message对象*/public static void addOffLineClientThreadMessage(String userID,Message message){//一开始存在离线线程if(chm.containsKey(userID)){ArrayList<Message> messageArrayList = chm.get(userID);messageArrayList.add(message);System.out.println("===================接着存入消息===================");}else{ //一开始不存在离线线程ArrayList<Message> messageArrayList = new ArrayList<>();messageArrayList.add(message);chm.put(userID,messageArrayList);System.out.println("【服务器已经存入 "+ message.getGetter() +" 的离线消息】");}}/*** 由用户ID获取对应的线程* @param userID* @return*/public static SeverConnectClientThread getSeverConnectClientThread(String userID){return hm.get(userID);}/*** 由用户ID获取离线的message对象数组* @param userID 离线的ID* @return*/public static Message[] getOffLineClientThread(String userID){ArrayList<Message> messageArrayList = chm.get(userID);Message[] messages = new Message[messageArrayList.size()];for (int i = 0; i < messageArrayList.size(); i++) {messages[i] = messageArrayList.get(i);}return messages;}/*** 由于客户端ID移除对应服务器Hashmap集合中的线程* @param userID*/public static void remove(String userID){hm.remove(userID);}/*** 由于客户端ID移除对应服务器 ConcurrentHashMap 集合中的离线线程* @param userID 用户ID*/public static void removeOffLineClientThread(String userID){chm.remove(userID);}/*** 服务器返回在线用户列表* @return*/public static String getOnlineUserList(){Set<String> keySet = hm.keySet();Iterator<String> iterator = keySet.iterator();String str = "";while (iterator.hasNext()){str += iterator.next().toString() + " ";}return str;}
}

QQServer

package Service;import commen.Message;
import commen.MessageType;
import commen.User;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;/*** @Package: Service* @ClassName: QQServer* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/17 15:53* @Description: miniQQ 服务器,在监听9999,等待客户端的连接,并保持通信*/
public class QQServer {private ServerSocket ss = null;/*** 创建一个集合,保存多个合法用户* 这里也可以使用 ConcurrentHashMap,可以处理并发的集合,没有线程安全* HashMap没有处理线程安全,因此在多线程情况下是不安全* ConcurrentHashMap处理的线程安全,即线程同步处理,在多线程情况下是安全* 这里只是对集合进行读的操作,故使用两者都可以,在写的时候使用 ConcurrentHashMap*/private static ConcurrentHashMap<String , User> validUsers =new ConcurrentHashMap<>();//用静态代码块初始化一次 validUsersstatic {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"));}/*** 检验用户是否合法* @param userID* @param pswd* @return*/public boolean isCheckUserValid(String userID,String pswd){User user = validUsers.get(userID);if(user == null){  //账户不存在return false;}if(!user.getPasswd().equals(pswd)){  //账户存在但密码不对return false;}return true;}/*** 构造器*/public QQServer() {//端口可以写在配置文件中try {System.out.println("服务器在9999监听......");ss = new ServerSocket(9999);//启动推送消息的线程new Thread(new SendNewsToAllClientService()).start();//当和某个客户端连接后,用while循环继续监听while (true){Socket socket = ss.accept();//得到Socket关联的对象流ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());User user = (User) ois.readObject();ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());//用于回复客户端消息的Message对象Message message = new Message();//验证账号和密码if(isCheckUserValid(user.getUserID(),user.getPasswd())){//将Message对象回复给客户端message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);oos.writeObject(message);//创建一个线程,与客户端保持通讯,该线程SeverConnectClientThread severConnectClientThread =new SeverConnectClientThread(socket, user.getUserID());//启动线程severConnectClientThread.start();//将线程加入到服务器中的集合中进行管理ManageClientThread.addClientThread(user.getUserID(), severConnectClientThread);} else{  //验证失败System.out.println("用户:"+user.getUserID()+" 验证信息失败!");//将Message对象回复给客户端message.setMesType(MessageType.MESSAGE_LOGIN_FAIL);oos.writeObject(message);//登录失败关闭该 socketsocket.close();}}} catch (Exception e) {e.printStackTrace();} finally {//如果服务器停止,则关闭 ServerSockettry {ss.close();} catch (IOException e) {e.printStackTrace();}}}
}

SendNewsToAllClient

package Service;import commen.Message;
import commen.MessageType;
import utility.Utility;import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;/*** @Package: Service* @ClassName: SendNewsToAllClient* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/20 22:06* @Description: 推送消息给所有客户端的服务*/
public class SendNewsToAllClientService implements Runnable{@Overridepublic void run() {while (true){System.out.println("请输入要推送的消息[输入exit退出]:");String news = Utility.readString(100);if(news.equals("exit")){break;}//构建一个群发的消息Message message = new Message();message.setMesType(MessageType.MESSAGE_TO_ALL_MESSAGE);message.setSender("服务器");message.setContent(news);message.setSendTime(new Date().toString());System.out.println("【服务器已经推送了消息】");//遍历在线用户HashMap<String, SeverConnectClientThread> hm = ManageClientThread.getHm();Iterator<String> iterator = hm.keySet().iterator();while (iterator.hasNext()){String onLineUsers = iterator.next();SeverConnectClientThread thread =ManageClientThread.getSeverConnectClientThread(onLineUsers);try {ObjectOutputStream oos =new ObjectOutputStream(thread.getSocket().getOutputStream());oos.writeObject(message);} catch (IOException e) {e.printStackTrace();}}}}
}

SeverConnectClientThread

package Service;import commen.Message;
import commen.MessageType;import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;/*** @Package: Service* @ClassName: SeverConnectClientThread* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/17 16:33* @Description: 该类的一个对象和某个客户端保持通讯*/
public class SeverConnectClientThread extends Thread{private Socket socket;//连接到服务器的用户IDprivate String userID;/*** 构造器* @param socket* @param userID*/public SeverConnectClientThread(Socket socket, String userID) {this.socket = socket;this.userID = userID;}public Socket getSocket() {return socket;}/*** 这里线程处于run状态,可以接受和发送消息*/@Overridepublic void run() {while(true){try {System.out.println("服务器与客户端 "+ userID +" 保持通讯,读取数据.......");ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());Message message = (Message) ois.readObject();//收到客户端Message做出对应判断if(message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)){//从服务器获取在线用户列表System.out.println("客户端 "+ message.getSender() +" 想要获取在线用户");String onlineUserList = ManageClientThread.getOnlineUserList();//构建Message对象返回给客户端Message message2 = new Message();message2.setMesType(MessageType.MESSAGE_RETURN_ONLINE_FRIEND);message2.setContent(onlineUserList);message2.setGetter(message.getSender());//创建数据通道返回messageObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(message2);}else if(message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)){System.out.println("客户端 " + message.getSender()+" 请求退出------>");//将这个客户端对应的线程从集合中移除ManageClientThread.remove(message.getSender());//关闭socket连接socket.close();//关闭改线程break;}else if(message.getMesType().equals(MessageType.MESSAGE_COMMON_MESSAGE)){//此处先判断服务器集合中目标客户端是否存在HashMap<String, SeverConnectClientThread> hm = ManageClientThread.getHm();//如果管理线程的集合中存在目标线程if(hm.containsKey(message.getGetter())){//在服务器的管理线程的集合中,获取私聊目标对象的线程SeverConnectClientThread severConnectClientThread =ManageClientThread.getSeverConnectClientThread(message.getGetter());//获取目标线程对应的socketSocket socket = severConnectClientThread.getSocket();//创建数据通道ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());//服务器转发消息给目标客户端oos.writeObject(message);}else{  //如果管理线程的集合中不存在目标线程ManageClientThread.addOffLineClientThreadMessage(message.getGetter(),message);}}else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MESSAGE)){HashMap<String, SeverConnectClientThread> hm = ManageClientThread.getHm();//遍历集合Iterator<String> iterator = hm.keySet().iterator();while (iterator.hasNext()){String onLineID = iterator.next();if(!onLineID.equals(message.getSender())){SeverConnectClientThread thread = hm.get(onLineID);ObjectOutputStream oos = new ObjectOutputStream(thread.getSocket().getOutputStream());oos.writeObject(message);}}}else if(message.getMesType().equals(MessageType.MESSAGE_FILE_MESSAGE)){SeverConnectClientThread thread =ManageClientThread.getSeverConnectClientThread(message.getGetter());Socket socket = thread.getSocket();ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(message);}else if(message.getMesType().equals(MessageType.MESSAGE_UnLine_MESSAGE)){//如果存在离线消息if(ManageClientThread.getChm().containsKey(message.getSender())){Message[] messages = ManageClientThread.getOffLineClientThread(message.getSender());SeverConnectClientThread thread =ManageClientThread.getSeverConnectClientThread(message.getSender());Socket socket = thread.getSocket();for (int i = 0; i < messages.length; i++) {ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());Message message1 = messages[i];message1.setMesType(MessageType.MESSAGE_UnLine_MESSAGE);oos.writeObject(message1);}System.out.println("==============服务器发送离线消息完毕============");ManageClientThread.removeOffLineClientThread(message.getSender());}}else{System.out.println("其它业务");}} catch (Exception e) {e.printStackTrace();}}}
}

qqFrame

package QQFrame;import Service.QQServer;/*** @Package: QQFrame* @ClassName: qqFrame* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/17 19:17* @Description: 创建qq服务器对象,并启动*/
public class qqFrame {public static void main(String[] args) {QQServer qqServer = new QQServer();}
}

三、Client

ClinetConnectServerThread

package Service;import commen.Message;
import commen.MessageType;import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;/*** @Package: Service* @ClassName: ClinetConnectServerThread* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/16 22:38* @Description: 客户端连接服务器的线程类*/
public class ClinetConnectServerThread extends Thread{//Socketprivate Socket socket;public ClinetConnectServerThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {//因为这个线程里面有一个Socket,用来一直与服务器保持通信,故用while循环while (true){try {ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());//如果服务器没有消息会一直阻塞在下面这里Message message = (Message) ois.readObject();//将从服务器收到的消息做处理,做出对应的业务逻辑if(message.getMesType().equals(MessageType.MESSAGE_RETURN_ONLINE_FRIEND)){//获取信息内容并做出对应消息处理String content = message.getContent();String[] onlineUsers = content.split(" ");System.out.println("\n\n------------在线用户------------");for (int i = 0; i < onlineUsers.length; i++) {System.out.println("用户:"+onlineUsers[i]);}}else if(message.getMesType().equals(MessageType.MESSAGE_COMMON_MESSAGE)){System.out.println("\n"+message.getSendTime());System.out.println("【"+message.getSender()+" 对 "+ message.getGetter()+" 说:】 "+message.getContent());}else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MESSAGE)){System.out.println("\n【" + message.getSender()+" 对大家说:】 "+ message.getContent());}else if(message.getMesType().equals(MessageType.MESSAGE_FILE_MESSAGE)){System.out.println("\n"+message.getSender()+" 发送文件到自己的 "+message.getDest());FileOutputStream fos = new FileOutputStream(message.getDest());fos.write(message.getFileBytes());fos.close();System.out.println("\n文件保存成功~~");}else if(message.getMesType().equals(MessageType.MESSAGE_UnLine_MESSAGE)){System.out.println("\n"+message.getSendTime());System.out.println("【"+message.getSender()+" 留言说:】"+message.getContent());}else{System.out.println("其他业务....");}} catch (Exception e) {e.printStackTrace();}}}public Socket getSocket() {return socket;}public void setSocket(Socket socket) {this.socket = socket;}
}

FileClientService

package Service;import commen.Message;
import commen.MessageType;import java.io.*;
import java.net.Socket;/*** @Package: Service* @ClassName: FileClientService* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/20 19:13* @Description: 客户端到服务器文件传输*/
public class FileClientService {public void sendFileToOne(String src,String dest,String senderID,String getterID){Message message = new Message();message.setSender(senderID);message.setGetter(getterID);message.setSrc(src);message.setDest(dest);message.setMesType(MessageType.MESSAGE_FILE_MESSAGE);//从src读取文件到客户端程序FileInputStream fileInputStream = null;byte[] filebytes = new byte[(int) new File(src).length()];try {fileInputStream = new FileInputStream(src);fileInputStream.read(filebytes);message.setFileBytes(filebytes);} catch (IOException e) {e.printStackTrace();} finally {if(fileInputStream!= null){try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}}//发送message对象ClinetConnectServerThread thread =ManageClinetConnectServerThread.getClinetConnectServerThread(senderID);Socket socket = thread.getSocket();try {ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(message);} catch (IOException e) {e.printStackTrace();}//提示信息System.out.println("【已经发送文件给" + getterID + "】");}
}

ManageClinetConnectServerThread

package Service;import java.util.HashMap;/*** @Package: Service* @ClassName: ManageClinetConnectServerThread* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/17 10:15* @Description:  客户端---管理客户端连接到服务器的线程*/
public class ManageClinetConnectServerThread {//创建hashmap来存储键值对,key - UserID  Value - 线程private static HashMap<String,ClinetConnectServerThread> hm = new HashMap<>();//将线程加入集合中public static void addClinetConnectServerThread(String uerID,ClinetConnectServerThread clinetConnectServerThread){hm.put(uerID,clinetConnectServerThread);}//通过uerID获取对应的线程public static ClinetConnectServerThread getClinetConnectServerThread(String uerID){return hm.get(uerID);}
}

MessageClientService

package Service;import commen.Message;
import commen.MessageType;import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;/*** @Package: Service* @ClassName: MessageClientService* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/20 11:03* @Description: 改类实现客户端消息与服务端消息的处理*/
public class MessageClientService {/*** 私发消息* @param sendID   发送者* @param getterID 接收者* @param content  内容*/public void sendMessageToOne(String sendID,String getterID,String content){//构建message对象Message message = new Message();message.setMesType(MessageType.MESSAGE_COMMON_MESSAGE);message.setSender(sendID);message.setGetter(getterID);message.setContent(content);message.setSendTime(new Date().toString());System.out.println("【"+ sendID +" 对 "+ getterID +" 的消息已发送】");//发送message对象try {ObjectOutputStream oos = new ObjectOutputStream(ManageClinetConnectServerThread.getClinetConnectServerThread(sendID).getSocket().getOutputStream());oos.writeObject(message);} catch (IOException e) {e.printStackTrace();}}/*** 群发消息* @param sendID 发送者* @param content 内容*/public void sendMessagetoAll(String sendID,String content){//构建message对象Message message = new Message();message.setMesType(MessageType.MESSAGE_TO_ALL_MESSAGE);message.setSender(sendID);message.setContent(content);message.setSendTime(new Date().toString());System.out.println("【"+ sendID +" 对大家的消息已发送】");//发送message对象try {ObjectOutputStream oos = new ObjectOutputStream(ManageClinetConnectServerThread.getClinetConnectServerThread(sendID).getSocket().getOutputStream());oos.writeObject(message);} catch (IOException e) {e.printStackTrace();}}/*** 查看自己的离线消息* @param userID*/public void lookUnlineMessage(String userID){Message message = new Message();message.setMesType(MessageType.MESSAGE_UnLine_MESSAGE);message.setSender(userID);message.setGetter("服务器");try {ObjectOutputStream oos = new ObjectOutputStream(ManageClinetConnectServerThread.getClinetConnectServerThread(userID).getSocket().getOutputStream());oos.writeObject(message);} catch (IOException e) {e.printStackTrace();}System.out.println(">>>>>>>>>>>>>>>(离线消息)<<<<<<<<<<<<<");}
}

UserClientService

package Service;import commen.Message;
import commen.MessageType;
import commen.User;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;/*** @Package: Service* @ClassName: UserClientService* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/16 22:18* @Description: 客户端服务---用于校验用户密码账号是否正确、以及注册账号密码*/
public class UserClientService {//用户对象private User user = new User();//socket对象private Socket socket;/*** 根据userID与pwd到服务器校验是否合法* @param userID* @param pwd* @return*/public boolean checkUser(String userID,String pwd){//判断账户是否验证成功boolean flag = false;//创建用户对象user.setUserID(userID);user.setPasswd(pwd);//连接到服务器,发送user对象try {socket = new Socket(InetAddress.getByName("169.254.25.66"),9999);//客户端发送User对象到数据通道ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(user);//客户端从数据通道读取服务器返回的信息ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());Message message = (Message) ois.readObject();//对返回的信息进行判断if(message.getMesType().equals(MessageType.MESSAGE_LOGIN_SUCCEED)){ //登录成功//此处需要一个持有Socket的线程,来保持和客户端的通信ClinetConnectServerThread clinetConnectServerThread =new ClinetConnectServerThread(socket);//启动线程clinetConnectServerThread.start();//将线程加入到集合中ManageClinetConnectServerThread.addClinetConnectServerThread(userID,clinetConnectServerThread);flag = true;}else{  //登录失败,关闭socketsocket.close();}} catch (Exception e) {e.printStackTrace();}return flag;}/*** 向服务器获取在线用户列表的方法*/public void onlineFrindList(){//准备message信息Message message = new Message();message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);message.setSender(user.getUserID());//发送给服务器//1. 从客户端管理线程的集合中,拿到当前对应线程ClinetConnectServerThread thread = ManageClinetConnectServerThread.getClinetConnectServerThread(user.getUserID());//2. 从改线程获取 SocketSocket socket = thread.getSocket();//3. 用 Socket 创建数据通道发送消息try {ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(message);} catch (IOException e) {e.printStackTrace();}}/*** 客户端退出,并且向服务器发送退出请求*/public void logout(){//构建退出请求Message message = new Message();message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);message.setSender(user.getUserID());//发送messagetry {//ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());ObjectOutputStream oos = new ObjectOutputStream(ManageClinetConnectServerThread.getClinetConnectServerThread(user.getUserID()).getSocket().getOutputStream());oos.writeObject(message);System.out.println(">>>>>>>>客户端 "+ user.getUserID() +" 退出系统<<<<<<<<");//直接结束客户端进程,其所有线程也会自动结束System.exit(0);} catch (IOException e) {e.printStackTrace();}}}

qqView

package View;import Service.FileClientService;
import Service.MessageClientService;
import Service.UserClientService;
import Utility.Utility;/*** @Package: View* @ClassName: qqView* @Author: 爱吃凉拌辣芒果* @CreateTime: 2021/11/16 14:12* @Description:*/
public class qqView {private boolean loop = true;//该对象用于登录验证/注册用户/对应服务private UserClientService userClientService = new UserClientService();//用于用户聊天private MessageClientService messageClientService = new MessageClientService();//用于文件传输private FileClientService fileClientService = new FileClientService();public static void main(String[] args) {new qqView().mainMenu();}/*** 主菜单*/private void mainMenu(){while (loop){System.out.println("============欢迎登录MiniQQ============");System.out.println("\t\t1 登录系统");System.out.println("\t\t9 退出系统");System.out.print("请输入:");String readString = Utility.readString(1);switch (readString){case "1":System.out.print("请输入用户账号:");String userName = Utility.readString(20);System.out.print("请输入用户密码:");String userPswd = Utility.readString(20);//此处将客户端输入的账号密码传输到服务器作验证if(userClientService.checkUser(userName,userPswd)){System.out.println(">>>>>>>>>>>>欢迎用户"+userName+"<<<<<<<<<<<<");while (loop){System.out.println("\n============小QQ二级菜单(用户"+userName+")============");System.out.println("\t\t1 显示在线用户列表");System.out.println("\t\t2 群发消息");System.out.println("\t\t3 私聊消息");System.out.println("\t\t4 发送文件");System.out.println("\t\t5 离线消息");System.out.println("\t\t9 退出系统");System.out.print("请输入你的选择:");int chooce = Utility.readInt();switch (chooce){case 1://编写方法从服务器获取用户列表userClientService.onlineFrindList();break;case 2:System.out.print("请输入想对大家说的话:");String readString1 = Utility.readString(50);messageClientService.sendMessagetoAll(userName,readString1);break;case 3:System.out.print("请输入想要聊天的用户(在线):");String getterID = Utility.readString(10);System.out.println("请输入想要要说的话:");String content = Utility.readString(50);//用方法实现发送messageClientService.sendMessageToOne(userName,getterID,content);break;case 4:System.out.println("请输入目标用户:");getterID = Utility.readString(40);System.out.println("请输入发送文件路径:");String src = Utility.readString(100);;System.out.println("请输入接收文件路径:");String dest = Utility.readString(100);fileClientService.sendFileToOne(src,dest,userName,getterID);break;case 5:messageClientService.lookUnlineMessage(userName);break;case 9:userClientService.logout();loop = false;break;}}}else{System.out.println("<<<<<<<<<<<<信息错误,登录失败>>>>>>>>>>>>");}break;case "9":loop = false;System.out.println("<<<<<<<<<<<<退出系统>>>>>>>>>>>>");break;default:System.out.println("<<<<<<<<<<<<输入错误>>>>>>>>>>>>");break;}}}
}

Java初学笔记30-【MiniQQ聊天部分代码】相关推荐

  1. 韩顺平Java自学笔记 项目 QQ聊天室

    目录 一.项目前的准备 1.为什么选择这个项目 2.项目开发的流程 3.项目的需求 二.开发阶段 1.登录功能实现 2.拉取在线用户实现 3.无异常退出的实现 4.私聊功能的实现 5.群发的实现 6. ...

  2. java学习笔记30(IO :缓冲流)

    缓冲流: 读取数据大量的文件时,读取的速度慢,java提供了一套缓冲流,提高IO流的效率: 缓冲流分为字节缓冲流和字符缓冲流: 字节输入缓冲流和字节输出缓冲流如下: package com.zs.De ...

  3. java后端系统学习总结 01_java第五版 java初学笔记,由浅入深

    文章目录 基本数据类型.引用数据类型(数组.类.接口->默认值都为null) **什么是引用** 堆.栈.引用变量: 数据类型详细介绍 整型(byte.short.int.long) 浮点型(f ...

  4. Java初学·笔记2

    变量和数据类型 1. 掌握使用IDE构建普通的Java项目 IDE:集成开发工具/环境 开发Java的主流IDE: eclipse 开源 由Eclipse软件基金会(不以盈利为目的)进行维护. mye ...

  5. Java初学笔记——18.Case12_个税计算

    package 控制流程;import java.text.NumberFormat; import java.util.Scanner;//已知://工资个税的计算公式为:应纳税额 = (工资薪金所 ...

  6. Java初学笔记——22.Case15_和女友对答

    package 控制流程;import java.util.Scanner;public class Case15_和女友对答 { public static void main(String[] a ...

  7. Java学习笔记30

    LinkedList类是List接口的实现类,它是一个List集合,可以根据索引来随机访问集合元素.除此之外,LinkedList还实 现了Deque接口,因此它可以被当成双端队列来使用,也可以被当成 ...

  8. Java基类共同属性设置_多选择基类的访问属性-Java初学笔记

    多选择基类的访问属性 你现在知道在定义类的访间属性时可用的选择项,你希望使用这些类定义子类.你知道在类继承上这些属性所具有的效果,但是你如何决定到底应该使用哪一个呢? 这里没有死板和现成的规则,你选择 ...

  9. JAVA初学笔记重点——子类与继承

    1.关键词:extends 2.格式:class 子类名 extends 父类名{ ...... } 3.每个类(除Object类)有且只有一个父类,一个类可以有多个或零个子类. 4.子类和父类在同一 ...

  10. Java初学笔记——4.AA制

    package AA制; //假设菜的价格都是整数,定义3个int表示菜的价格 //一共三个人一起吃饭,他们决定AA制 //请帮他们算出每个人付多少钱 public class Case1_AA制 { ...

最新文章

  1. F41G-UT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
  2. 考究Hadoop中split的计算方法
  3. popstate_HTML onpopstate 属性 - Break易站
  4. websocket没准备好如何解决_那些很重要,但是不常用的技术,websocket
  5. 【渝粤题库】陕西师范大学210001儿童心理学 作业(高起专、高起本)
  6. LeetCode 461. Hamming Distance
  7. 解决 RaspberryPi 树莓派 NTP服务异常 无法自动同步时间
  8. 迁移学习和数据扩充(附代码)
  9. 新安装IAR打开现有工程
  10. 我的世界起床战争php,我的世界起床战争最新版下载-我的世界起床战争手机版v1.21.5.115731 安卓版 - 极光下载站...
  11. linux 极路由救砖,极路由三不死u-boot刷机方法
  12. Cadence Allegro如何复用设计参数?
  13. 【SAP消息号KD503】
  14. 精练战略云 VS “华丽“战术云
  15. 画图形表格用ECharts
  16. Android数据库备份和恢复
  17. tif文件转换为CAD编辑操作
  18. 【算法】Sunday算法(模式匹配)
  19. The web application [] appears to have started a thread named [thread-0]
  20. 18001 Farmer Cat

热门文章

  1. matlab制作有值显示的条形统计图
  2. 为什么手机里的小爱音响app里搜不到家里的小爱音箱_水哥岁末诚意奉献:基于米家App的家庭智能安全方案详解...
  3. 我对管理和领导的理解
  4. maven报错The JAVA_HOME environment variable is not defined correctly
  5. 【C系列】How to printf “unsigned long” in C?
  6. 整合+策略:微网通联的GMP平台如何帮助企业搭建统一智慧通信架构
  7. 跟着安全牛大表哥学渗透
  8. 频域滤波—方波变换中的沃尔什变换与哈达玛变换
  9. navigateTo和navigateBack的使用
  10. Python3正则表达式:match函数