说明1:只需修改服务器端代码,客户端代码可以不用修改
说明2:离线消息的存储放在了ServerConnectClientThread 中处理私聊消息模块,只需经过一个判断就可以识别是在线消息还是离线消息,从而可以确定是存储还是直接转发
else if (ms.getMesType().equals(MessageType.MESSAGE_COMM_MES)) {
System.out.println(ms.getSender() + "向" + ms.getGetter() + "发送消息,消息内容保密");
if (!ManageServerConnectClientThread.hm.containsKey(ms.getGetter())) {
System.out.println(ms.getGetter() + "暂不在线,将在上线后发送");
ArrayList<Message> messages = new ArrayList<>();
if (QQServer.offlineDB.containsKey(ms.getGetter())) {
QQServer.offlineDB.get(ms.getGetter()).add(ms);
} else {
messages.add(ms);
QQServer.offlineDB.put(ms.getGetter(), messages);
}
} else {
// 此处为关键,获取ms的 getter 的socket
// getServerConnectClientThread()方法:根据当前的userId,获取对应的线程
Socket socket1 = getServerConnectClientThread(ms.getGetter()).socket;
// 本质上为转发
ObjectOutputStream oos = new ObjectOutputStream(socket1.getOutputStream());
oos.writeObject(ms);// 如果用户不在线,可以保存到数据库,这样就可以实现留言
}
}
说明3:用户上线后是否有自己的离线消息相关方法放在了新建类OfflineMessage 中,offline()方法提供遍历集合,离线消息转发功能等功能
public class OfflineMessage {
public void offline(String userId, Socket socket) {
// 登录成功后遍历db集合查看是否有离线消息
// ConcurrentHashMap.KeySetView<String, ArrayList> keySet = offlineDB.keySet();
// 遍历集合,得到getterId
ConcurrentHashMap.KeySetView<String, ArrayList<Message>> getterIds = QQServer.offlineDB.keySet();
for (String getterId : getterIds) {
if (getterId.equals(userId)) {
System.out.println("数据库中有用户" + getterId + "的离线消息");
// 根据userId 取出对应的ArrayList<Message>
ArrayList<Message> messages = QQServer.offlineDB.get(getterId);
for (int i = 0; i < messages.size(); i++) {
Message message = messages.get(i);
try {
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 消息转发成功,从数据库中删除消息
QQServer.offlineDB.remove(getterId);
}
}
}
说明4:只需要在用户登录后调用offline()方法即可,注意:一定要在服务器返回用户登录成功消息之后再去调用,不然会出现invalid type code: AC异常