17.4.3 使用MulticastSocket实现多点广播(5)

网友投稿 238 2022-10-22


17.4.3 使用MulticastSocket实现多点广播(5)

该类主要实现底层的网络通信功能,在该类中提供了一个broadCast()方法,该方法使用Multicast Socket将指定字符串广播到所有客户端;还提供了sendSingle()方法,该方法使用DatagramSocket将指定字符串发送到指定SocketAddress,如程序中前两行粗体字代码所示。除此之外,该类还提供了两个内部线程类:ReadSingle和ReadBroad,这两个线程类采用循环不断地读取DatagramSocket和Multicast Socket中的数据,如果读到的信息是广播来的在线信息,则保持该用户在线;如果读到的是用户的聊天信息,则直接将该信息显示出来。 在该类中用到了本程序的一个主类:LanTalk,该类使用DefaultListModel来维护用户列表,该类里的每个列表项就是一个UserInfo。该类还提供了一个ImageCellRenderer,该类用于将列表项绘制出用户图标和用户名字。 程序清单:codes\17\17.4\LanTalk\LanTalk.java   public class LanTalk extends JFrame { private DefaultListModel listModel = new DefaultListModel<>(); // 定义一个JList对象 private JList friendsList = new JList<>(listModel); // 定义一个用于格式化日期的格式器 private DateFormat formatter = DateFormat.getDateTimeInstance(); public LanTalk() { super("局域网聊天"); // 设置该JList使用ImageCellRenderer作为单元格绘制器 friendsList.setCellRenderer(new ImageCellRenderer()); listModel.addElement(new UserInfo("all" , "所有人" , null , -2000)); friendsList.addMouseListener(new ChangeMusicListener()); add(new JScrollPane(friendsList)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(2, 2, 160 , 600); } // 根据地址来查询用户 public UserInfo getUserBySocketAddress(SocketAddress address) { for (int i = 1 ; i < getUserNum() ; i++) { UserInfo user = getUser(i); if (user.getAddress() != null && user.getAddress().equals(address)) { return user; } } return null; } // ------下面四个方法是对ListModel的包装------ // 向用户列表中添加用户 public void addUser(UserInfo user) { listModel.addElement(user); } // 从用户列表中删除用户 public void removeUser(int pos) { listModel.removeElementAt(pos); } // 获取该聊天窗口的用户数量 public int getUserNum() { return listModel.size(); } // 获取指定位置的用户 public UserInfo getUser(int pos) { return listModel.elementAt(pos); } // 实现JList上的鼠标双击事件监听器 class ChangeMusicListener extends MouseAdapter { public void mouseClicked(MouseEvent e) { // 如果鼠标的击键次数大于2 if (e.getClickCount() >= 2) { // 取出鼠标双击时选中的列表项 UserInfo user = (UserInfo)friendsList.getSelectedValue(); // 如果该列表项对应用户的交谈窗口为null if (user.getChatFrame() == null) { // 为该用户创建一个交谈窗口,并让该用户引用该窗口 user.setChatFrame(new ChatFrame(null , user)); } // 如果该用户的窗口没有显示,则让该用户的窗口显示出来 if (!user.getChatFrame().isShowing()) { user.getChatFrame().setVisible(true); } } } } /** * 处理网络数据报,该方法将根据聊天信息得到聊天者 * 并将信息显示在聊天对话框中 * @param packet 需要处理的数据报 * @param single 该信息是否为私聊信息 */ public void processMsg(DatagramPacket packet , boolean single) { // 获取发送该数据报的SocketAddress InetSocketAddress srcAddress = (InetSocketAddress) packet.getSocketAddress(); // 如果是私聊信息,则该Packet获取的是DatagramSocket的地址 // 将端口号减1才是对应的MulticastSocket的地址 if (single) { srcAddress = new InetSocketAddress(srcAddress.getHostName() , srcAddress.getPort() - 1); } UserInfo srcUser = getUserBySocketAddress(srcAddress); if (srcUser != null) { // 确定消息将要显示到哪个用户对应的窗口中 UserInfo alertUser = single ? srcUser : getUser(0); // 如果该用户对应的窗口为空,则显示该窗口 if (alertUser.getChatFrame() == null) { alertUser.setChatFrame(new ChatFrame(null , alertUser)); } // 定义添加的提示信息 String tipMsg = single ? "对您说:" : "对大家说:"; // 显示提示信息 alertUser.getChatFrame().addString(srcUser.getName() + tipMsg + "......................(" + formatter.format(new Date()) + ")\n" + new String(packet.getData() , 0 , packet.getLength()) + "\n"); if (!alertUser.getChatFrame().isShowing()) { alertUser.getChatFrame().setVisible(true); } } } // 主方法,程序的入口 public static void main(String[] args) { LanTalk lanTalk = new LanTalk(); new LoginFrame(lanTalk , "请输入用户名、头像后登录"); } } // 定义用于改变JList列表项外观的类 class ImageCellRenderer extends JPanel implements ListCellRenderer { private ImageIcon icon; private String name; // 定义绘制单元格时的背景色 private Color background; // 定义绘制单元格时的前景色 private Color foreground; @Override public Component getListCellRendererComponent(JList list , UserInfo userInfo , int index , boolean isSelected , boolean cellHasFocus) { // 设置图标 icon = new ImageIcon("ico/" + userInfo.getIcon() + ".gif"); name = userInfo.getName(); // 设置背景色、前景色 background = isSelected ? list.getSelectionBackground() : list.getBackground(); foreground = isSelected ? list.getSelectionForeground() : list.getForeground(); // 返回该JPanel对象作为单元格绘制器 return this; } // 重写paintComponent方法,改变JPanel的外观 public void paintComponent(Graphics g) { int imageWidth = icon.getImage().getWidth(null); int imageHeight = icon.getImage().getHeight(null); g.setColor(background); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(foreground); // 绘制好友图标 g.drawImage(icon.getImage() , getWidth() / 2 - imageWidth / 2 , 10 , null); g.setFont(new Font("SansSerif" , Font.BOLD , 18)); // 绘制好友用户名 g.drawString(name, getWidth() / 2 - name.length() * 10 , imageHeight + 30 ); } // 通过该方法来设置该ImageCellRenderer的最佳大小 public Dimension getPreferredSize() { return new Dimension(60, 80); } } 上面类中提供的addUser()和removeUser()方法暴露给通信类ComUtil使用,用于向用户列表中添加、删除用户。除此之外,该类还提供了一个processMsg()方法,该方法用于处理网络中读取的数据报,将数据报中的内容取出,并显示在特定的窗口中。 上面讲解的只是本程序的关键类,本程序还涉及YeekuProtocol、ChatFrame、LoginFrame等类,由于篇幅关系,此处不再给出这些类的源代码,读者可以参考codes\17\17.4\LanTalk路径下的源代码。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:springboot整合rocketmq实现分布式事务
下一篇:边缘计算如何实现5G的承诺
相关文章

 发表评论

暂时没有评论,来抢沙发吧~