思路分析:
客户端
服务端
功能实现
客户端
MessageClientService.java : 新增私聊类,与私聊方法
package com.ming.qqclient.service;
import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;
/**
* @Author: mei_ming
* @DateTime: 2022/10/3 15:33
* @Description: 这是一个提供和消息相关的服务类
*/
public class MessageClientService {
//私聊功能
public void sendMessageToOne(String content,String senderId,String getterId){
//构建Message
Message message = new Message();
message.setMesType(MessageType.MESSAGE_COMM_MES);
message.setSender(senderId);
message.setGetter(getterId);
message.setContent(content);
message.setSendTime(new Date().toString());
System.out.println(senderId+" 对 "+getterId+" 说 "+ content);
try {
// 发送到服务端
ObjectOutputStream oos =
new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
QQView.java : 调用私聊方法
switch (key){
case "1":
//调用UserClientService中的方法:
userClientService.onlineFriendList();
break;
case "2":
System.out.println("群发消息");
break;
case "3":
System.out.print("请输入想聊天的用户号(在线): ");
String getterId = Utility.readString(50);
System.out.print("请输入想说的话: ");
String content = Utility.readString(100);
//将消息发送给服务端
messageClientService.sendMessageToOne(content,userId,getterId);
break;
case "4":
System.out.println("发送文件");
break;
case "9":
//调用方法 退出
userClientService.logout();
loop=false;
break;
}
服务端
ServerConnectClientThread.java : 在run方法里面增加判断messageType,如果是MESSAGE_COMM_MES
则进行消息的转发
package com.ming.qqserver.service;
import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;
import com.sun.deploy.net.proxy.ProxyUnavailableException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
/**
* @Author: mei_ming
* @DateTime: 2022/10/2 21:09
* @Description: 该类的一个对象和某个客户端保持通信
*/
public class ServerConnectClientThread extends Thread {
private Socket socket;
private String userId; //区分是哪一个客户端
public Socket getSocket() {
return socket;
}
public ServerConnectClientThread(Socket socket, String userId) {
this.socket = socket;
this.userId = userId;
}
@Override
public void run() { //这里线程处于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)){
//客户端要在线用户列表
// 格式 "100 200 孙悟空"
System.out.println(message.getSender() + " 要在线用户列表");
String onlineUser = ManageClientThreads.getOnlineUser();
//返回message
Message message2 = new Message();
message2.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
message2.setContent(onlineUser);
message2.setGetter(message.getSender());
//返回客户端
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(message2);
}else if(message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
//私聊功能
//根据message获取getterId,然后得到对应的线程
ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());
//在线程里获取对应的socket,在获取socket对应的对象输出流
ObjectOutputStream oos = new ObjectOutputStream(serverConnectClientThread.getSocket().getOutputStream());
oos.writeObject(message); //转发, 如果客户不在线,可以把内容存到数据库, 做成离线发送消息
}else if(message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)){
//客户端退出功能
System.out.println(message.getSender()+" 退出");
//将这个客户端对应的线程从集合中删除
ManageClientThreads.removeServerConnectClientThread(message.getSender());
socket.close(); //关闭连接
break; //退出线程
}else{
System.out.println("是其他类型的message,暂不处理");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
运行效果
和私聊功能类似
共通
MessageType.java : 增加群发的MessageType
package com.ming.qqcommon;
/**
* @Author: mei_ming
* @DateTime: 2022/10/2 18:31
* @Description: 表示消息类型
*/
public interface MessageType {
String MESSAGE_LOGIN_SUCCEED = "1"; // 登录成功
String MESSAGE_LOGIN_FAIL = "2"; // 登录失败
String MESSAGE_COMM_MES = "3"; //普通信息包
String MESSAGE_GET_ONLINE_FRIEND = "4"; //要求返回在线用户列表
String MESSAGE_RET_ONLINE_FRIEND = "5"; //返回在线用户列表
String MESSAGE_CLIENT_EXIT = "6"; //客户端请求退出
String MESSAGE_TO_ALL_MES = "7"; //群发的消息
}
客户端
QQView.java : 修改case2为群发的功能,调用sendMessageToAll()方法
switch (key){
case "2":
System.out.print("请输入想说的话: ");
String contentForAll = Utility.readString(100);
//将消息发送给服务端
messageClientService.sendMessageToAll(contentForAll,userId);
break;
}
MessageClientService.java : 新增sendMessageToAll()方法
package com.ming.qqclient.service;
import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Date;
/**
* @Author: mei_ming
* @DateTime: 2022/10/3 15:33
* @Description: 这是一个提供和消息相关的服务类
*/
public class MessageClientService {
//群聊功能
public void sendMessageToAll(String content,String senderId){
//构建Message
Message message = new Message();
message.setMesType(MessageType.MESSAGE_TO_ALL_MES);
message.setSender(senderId);
message.setContent(content);
message.setSendTime(new Date().toString());
System.out.println(senderId+" 对大家说 "+ content);
try {
// 发送到服务端
ObjectOutputStream oos =
new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
}
//私聊功能 senderId : 发送者 , getterId : 接收者
public void sendMessageToOne(String content,String senderId,String getterId){
//构建Message
Message message = new Message();
message.setMesType(MessageType.MESSAGE_COMM_MES);
message.setSender(senderId);
message.setGetter(getterId);
message.setContent(content);
message.setSendTime(new Date().toString());
System.out.println(senderId+" 对 "+getterId+" 说 "+ content);
try {
// 发送到服务端
ObjectOutputStream oos =
new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
ClientConnectServerThread.java :判断服务端返回type是否是MESSAGE_TO_ALL_MES
package com.ming.qqclient.service;
import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
/**
* @Author: mei_ming
* @DateTime: 2022/10/2 20:25
* @Description: 线程生成类
*/
public class ClientConnectServerThread extends Thread{
//该线程需要持有Socket
private Socket socket;
//构造器可以接受一个Socket对象
public ClientConnectServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//因为Thread需要在后台和服务器通信,所以用while
while(true){
try {
System.out.println("客户端线程,等待读取从服务端发送的消息");
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message message = (Message) ois.readObject();
//读取message,判断类型,做相应的处理
if (message.getMesType().equals(MessageType.MESSAGE_RET_ONLINE_FRIEND)){
//读取到 '在线用户列表'对应的值
//取出在线列表,并打印
String[] onlineUsers = message.getContent().split(" ");
System.out.println("\n========当前在线用户列表========");
for (int i = 0; i <onlineUsers.length ; i++) {
System.out.println("用户: "+onlineUsers[i]);
}
}else if (message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
//把从服务器转发的消息,显示到控制台即可
System.out.println("\n"+ message.getSendTime()+" "+ message.getSender()+" 对 "+
message.getGetter()+" 说: "+message.getContent());
} else if (message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){
//显示在客户端的控制台
System.out.println("\n"+ message.getSender()+"对大家说: "+message.getContent());
}else{
System.out.println("是其他类型的message,暂不处理");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
}
服务端
ServerConnectClientThread.java : 判断客户端发送的type是否为MESSAGE_TO_ALL_MES
package com.ming.qqserver.service;
import com.ming.qqcommon.Message;
import com.ming.qqcommon.MessageType;
import com.sun.deploy.net.proxy.ProxyUnavailableException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
/**
* @Author: mei_ming
* @DateTime: 2022/10/2 21:09
* @Description: 该类的一个对象和某个客户端保持通信
*/
public class ServerConnectClientThread extends Thread {
private Socket socket;
private String userId; //区分是哪一个客户端
public Socket getSocket() {
return socket;
}
public ServerConnectClientThread(Socket socket, String userId) {
this.socket = socket;
this.userId = userId;
}
@Override
public void run() { //这里线程处于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)){
//客户端要在线用户列表
// 格式 "100 200 孙悟空"
System.out.println(message.getSender() + " 要在线用户列表");
String onlineUser = ManageClientThreads.getOnlineUser();
//返回message
Message message2 = new Message();
message2.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
message2.setContent(onlineUser);
message2.setGetter(message.getSender());
//返回客户端
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(message2);
}else if(message.getMesType().equals(MessageType.MESSAGE_COMM_MES)){
//私聊功能
//根据message获取getterId,然后得到对应的线程
ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(message.getGetter());
//在线程里获取对应的socket,在获取socket对应的对象输出流
ObjectOutputStream oos = new ObjectOutputStream(serverConnectClientThread.getSocket().getOutputStream());
oos.writeObject(message); //转发, 如果客户不在线,可以把内容存到数据库, 做成离线发送消息
}else if(message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)){
//群发消息功能
//需要遍历 管理线程的集合,把所有线程的socket得到,把message转发到其他线程中
HashMap<String, ServerConnectClientThread> hm = ManageClientThreads.getHm();
Iterator<String> iterator = hm.keySet().iterator();
while(iterator.hasNext()){
//取出在线用户的id
String onlineUser = iterator.next().toString();
if (!onlineUser.equals(message.getSender())){ //排除群发消息的用户
// 发送message
ServerConnectClientThread serverConnectClientThread = ManageClientThreads.getServerConnectClientThread(onlineUser);
OutputStream outputStream = serverConnectClientThread.getSocket().getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
oos.writeObject(message);
}
}
}else if(message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)){
//客户端退出功能
System.out.println(message.getSender()+" 退出");
//将这个客户端对应的线程从集合中删除
ManageClientThreads.removeServerConnectClientThread(message.getSender());
socket.close(); //关闭连接
break; //退出线程
}else{
System.out.println("是其他类型的message,暂不处理");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
运行效果