在线用户给离线用户发送消息
在下面代码中,仅仅展示私聊时,在线用户给离线用户发送消息,不考虑群发消息或者文件发送。
因为消息的接收者不在线,所以服务端线程集合里,不存在消息接收者的线程。故而这里会爆一个空指针异常

为了解决这个问题,我们就要在服务端线程里的私聊代码块中判断消息接收者的线程是否存在。
下面的是我修改之后的服务器线程关于私聊的代码块
if (message.getMesType().equals(MessageType.MESSAGE_COME_MES)) {System.out.println(message.getSender()+"发送给" + message.getGetter() + "的消息:"+message.getContent());//得到接收者的输出流,将得到的信息返回if (ManageClientThreads.getServerConnectClientThread(message.getGetter())==null){//假如用户不在线//把消息存放进一个集合里,等到用户登陆时判断集合是否有自己没有接收到的信息,有的话取出来NoOnlineUserMessage.hashMap.put(message.getGetter(),message);}else {ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientThreads.getServerConnectClientThread(message.getGetter()).getSocket().getOutputStream());objectOutputStream.writeObject(message);//加入客户不在线,可以保存到数据库,可以实现离线留言}}
大家可能已经注意到了,我将在线用户发送给离线用户的消息存放进了一个集合里面,之后离线用户上线时,可以通过这个集合来获取自己因为离线而没有收到的消息,我新开了一个类,专门存放这个集合。
集合的key是消息的接收者,也就是离线用户的id,value是消息本身
package QQServer.Service;import QQCommon.Message;import java.util.HashMap;public class NoOnlineUserMessage {public static HashMap<String, Message> hashMap=new HashMap<>();
}
以上就是在线用户给离线用户发送消息的全部了,接下来我将叙述离线用户如何获取集合里的消息
离线用户接收自己离线时的消息
我把这个功能放进了用户的二级菜单里面了
我的二级菜单
System.out.println("============网络通信系统二级菜单============");System.out.println("\t\t 1 显示在线用户列表");System.out.println("\t\t 2 群发消息");System.out.println("\t\t 3 私聊消息");System.out.println("\t\t 4 发送文件");System.out.println("\t\t 5 查看自己离线时没有收到的信息");System.out.println("\t\t 9 退出系统");System.out.println("请输入您的选择:~~~~");
之后在switch函数里的case新加一个判断:
case "5":System.out.println("查看自己离线时没有接收到的信息");messageClientService.LookMyNoLookMessage(userID);break;
我把这个LookMyNoLookMessage(String sender)方法写进了发送信息的服务类(MessageClientService)了
LookMyNoLookMessage(String sender):我们定义一个新的消息,我们需要设置消息的类型以及消息的发送者,发送给服务器端。这个消息的主要作用就是将消息的发送者与服务器端的集合的key进行比对,类似于小蝌蚪找妈妈
public void LookMyNoLookMessage(String sender){Message message = new Message();message.setSender(sender);message.setMesType(MessageType.MESSAGE_LOOK_MY_NO_LOOK);try {ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServiceThread(message.getSender()).getSocket().getOutputStream());objectOutputStream.writeObject(message);} catch (IOException e) {e.printStackTrace();}}
需要注意的是,因为这是一个全新的方法,所以我在消息类型里面新定义了一个Type。
以下代码是我的MessageType,类型9就是我新定义的,一定注意,客户端与服务端的MessageType一定要保持一致
package QQCommon;public interface MessageType {//在接口里面定义常量,不同的常量代表不同的消息类型String MESSAGE_LOGIN_SUCCEED = "1";//登录成功String MESSAGE_LOGIN_FAIL="2";//登录失败String MESSAGE_COME_MES="3";//普通信息包String MESSAGE_GET_ONLINE_FRIEND="4";//要求返回用户列表String MESSAGE_RET_ONLINE_FRIEND="5";//返回在线用户列表String MESSAGE_CLIENT_EXIT="6";//客户端要求退出String MESSAGE_GROUP_CHAT="7";//群聊String MESSAGE_SEND_FILES="8";//发送文件String MESSAGE_LOOK_MY_NO_LOOK="9";//查看离线时没有接收到的信息
}
我们调用这个函数之后,服务器端会接收到一个消息,我们对消息的类型进行判断。
遍历集合,找到与发送者一样的key,得到发送者因为离线而没有收到的消息,之后得到发送者的输出流,输出流输出我们在集合找到的message,重新发送给客户端
if (message.getMesType().equals(MessageType.MESSAGE_LOOK_MY_NO_LOOK)){Iterator<String> iterator = NoOnlineUserMessage.hashMap.keySet().iterator();while (iterator.hasNext()) {String next = iterator.next().toString();if (next.equals(message.getSender())){//NoOnlineUserMessage.hashMap.get(next)//这个就是没有接收到的信息ObjectOutputStream objectOutputStream = new ObjectOutputStream(ManageClientThreads.getServerConnectClientThread(next).getSocket().getOutputStream());objectOutputStream.writeObject(NoOnlineUserMessage.hashMap.get(next));}}}
客户端进行接收,并把消息打印到输出台上。
if (message.getMesType().equals(MessageType.MESSAGE_LOOK_MY_NO_LOOK)){System.out.println("这是你离线时没有接收到的信息:");System.out.println("发送者: " + message.getSender() + '\n' +"接收者: " + message.getGetter() + '\n' +"发送时间: " + message.getSendTime() + '\n' +"内容: " + message.getContent() + '\n');}
代码到这里基本就已经完成了,看看效果吧!
程序效果

大家可以看到,这里只运行了一个客户,在线客户给离线客户发送信息

这是服务器端的情况,可以看到正常,没有报错
之后我们登录用户200,查看离线时自己错过的信息

效果比较理想。
总结
总体思路就是,在线用户给离线用户发送信息,离线用户暂时收不到,我们便把消息放到一个集合里面,之后等离线用户上线后,对服务器端进行示意,表示自己已经上线,就想办法从集合里重新取出自己的消息,之后发给自己。
PS:
我的第一篇博客,如果大家对于这个内容有任何不明白的地方,请私信我,我将尽我全力无偿向大家解答。