一步一步写简易版飞鸽传书(三)

来源:互联网 发布:淘宝上传照片大小 编辑:程序博客网 时间:2024/06/06 19:27

实现公聊

一、前言

我们首先来回顾一下到目前为止我们已经完成的工作。我们已经完成登录窗口、好友列表窗口和聊天窗口以及一个通信类。当我们打开登录窗口,填好用户名选好头像之后,点登录按钮就会弹出一个好友列表窗口,当双击某个好友之后就会打开一个与他聊天的窗口。这里我们发现了一个问题,我们没有做控制。每次双击某个好友都会弹出一个新的聊天窗口。

这当然是不合适的,和谁聊天,我们都应该只打开一个窗口才对。改起来也比较简单,在用户类User中加一个ChatFrame 的实例,表明这个用户对应的聊天窗口。

   //该用户对应的聊天窗口private ChatFrame chatFrame;public ChatFrame getChatFrame() {return chatFrame;}public void setChatFrame(ChatFrame chatFrame) {this.chatFrame = chatFrame;}FriendListFrame 中ShowChatFrameListener 修改如下:   //实现JList上的鼠标双击事件的监听器class ShowChatFrameListener extends MouseAdapter{public void mouseClicked(MouseEvent e){//双击if(e.getClickCount() >= 2){User user = friendList.getSelectedValue();if(user.getChatFrame() == null){user.setChatFrame(new ChatFrame(user));}//如果该用户的聊天窗口没有显示,则让该用户的聊天窗口显示出来if (!user.getChatFrame().isShowing()){user.getChatFrame().setVisible(true);}}}}

这样就OK了。

二、处理用户上线、下线和公聊

接下来还有一个问题需要解决,那就是及时更新好友列表的问题。当有新用户上线时,我们要把他加入到好友列表中,当他下线时我们要将他从好友列表中剔除。实现的思路就是用户登录后就定时广播一条特殊的消息——在线消息,而用户关闭所有窗口退出后,显然就不会再广播这条消息了。关键的部分代码在这里再简单解释下,大部分都是直接拷贝自《疯狂java讲义》。

 

首先,用户类User中新增了两个属性,SocketAddress address 和 int lost。address 用来唯一地标识一个用户,lost标识该用户失去联系的次数,当失联次数大于2时就让他下线。

 

//该用户所在的IP和端口private SocketAddress address;//该用户失去联系的次数private int lost;//使用address作为该用户的标识,所以根据address作为//重写hashCode()和equals方法的标准public int hashCode(){return address.hashCode();}public boolean equals(Object obj){if(obj != null && obj instanceof User){User target = (User) obj;if(address != null){return address.equals(target.getAddress());}}return false;}

在登录按钮的事件中,我们构造一条特殊的广播消息——用户上线消息,然后定义一个定时器。登录按钮的事件代码如下:

  @Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stub//显示好友列表窗口friendListFrame = new FriendListFrame();loginFrame.setVisible(false);//实例化通信工具类comUtil = new ComUtil();//构造用户上线消息String userOnlineMsg = MyProtocol.PRESENCE+userNameField.getText()+MyProtocol.SPLITTER+iconList.getSelectedIndex()+".jpg"+MyProtocol.PRESENCE;comUtil.broadMsg(userOnlineMsg);// 启动定时器每3秒广播一次在线消息    Timer timer= new Timer(1000 * 3, new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {// TODO 自动生成的方法存根comUtil.broadMsg(userOnlineMsg);}});    timer.start();}

另外,我们定义了一个消息处理类MsgProcessor ,用它来处理消息。在其中,暂时提供一个处理公聊消息的方法,根据消息是普通公聊消息还是用户上线消息,分别做了相应的处理。代码如下:

package com.myipmsg.util;import java.io.UnsupportedEncodingException;import java.net.DatagramPacket;import java.net.SocketAddress;import java.util.ArrayList;import java.util.List;import com.myipmsg.bean.User;import com.myipmsg.comutil.ComUtil;import com.myipmsg.frame.ChatFrame;import com.myipmsg.frame.FriendListFrame;/** * 消息处理工具类 * @author ThinkPad * */public class MsgProcessor {/** * 处理公聊消息 * @param msg */public void processBroadMsg(DatagramPacket broaInPacket, FriendListFrame friendListFrame){try {String msg = new String(broaInPacket.getData(), 0, broaInPacket.getLength(),ComUtil.CHARSET);if(msg.startsWith(MyProtocol.PRESENCE) && msg.endsWith(MyProtocol.PRESENCE)){//说明是用户上线消息String userMsg = msg.substring(2, msg.length() - 2);String[] userInfo = userMsg.split(MyProtocol.SPLITTER);User user = new User(userInfo[0], userInfo[1], broaInPacket.getSocketAddress(),0);//表明该用户是否是刚刚上线,是否需要添加到好友列表中boolean needAdd = true; //保存需要被删除的用户下标List<Integer> toBeRemoveList = new ArrayList<>();for(int i=1;i<friendListFrame.getUserNum();i++){//从1开始,是因为好友列表中第一个是“所有用户”,让它一直在线User theUser = friendListFrame.getUser(i);//首先不管三七二十一,将它的失去联系次数加1theUser.setLost(theUser.getLost()+1);if(theUser.equals(user)){needAdd = false;//如果检测到了该用户,那么将他的失联次数置为0theUser.setLost(0);}//如果用户失联次数大于2,那么应该要将他从好友列表中删除if(theUser.getLost() > 2){toBeRemoveList.add(i);}}// 删除toBeRemoveList中的所有索引对应的用户for (int i = 0; i < toBeRemoveList.size() ; i++){friendListFrame.removeUser(toBeRemoveList.get(i));}//加入新上线的用户if(needAdd){friendListFrame.addUser(user);}}else{//说明是普通的公聊消息,那么打开公聊窗口,显示消息//第1个用户是所有人ChatFrame publicChatFrame = null;if(friendListFrame.getUser(0).getChatFrame() == null){publicChatFrame = new ChatFrame(friendListFrame.getUser(0)); friendListFrame.getUser(0).setChatFrame(publicChatFrame);}else{publicChatFrame = friendListFrame.getUser(0).getChatFrame();}if(!publicChatFrame.isShowing()){publicChatFrame.setVisible(true);}SocketAddress address = broaInPacket.getSocketAddress();publicChatFrame.addMsg(friendListFrame.findUserByAddress(address).getName()+" : "+msg);}} catch (UnsupportedEncodingException e) {// TODO 自动生成的 catch 块e.printStackTrace();}}public static void main(String[] args) {// TODO 自动生成的方法存根}}

注意:本篇为了实现公聊功能,很多类都有修改。这里不一一赘述,详细的代码请看附件。

所有代码可在此处下载:http://download.csdn.net/detail/zhutulang/9207885


0 0
原创粉丝点击