该类主要实现底层的网络通信功能,在该类中提供了一个broadCast()方法,该方法使用Multicast Socket将指定字符串广播到所有客户端;还提供了sendSingle()方法,该方法使用DatagramSocket将指定字符串发送到指定SocketAddress,如程序中前两行粗体字代码所示。除此之外,该类还提供了两个内部线程类:ReadSingle和ReadBroad,这两个线程类采用循环不断地读取DatagramSocket和Multicast Socket中的数据,如果读到的信息是广播来的在线信息,则保持该用户在线;如果读到的是用户的聊天信息,则直接将该信息显示出来。

在该类中用到了本程序的一个主类:LanTalk,该类使用DefaultListModel来维护用户列表,该类里的每个列表项就是一个UserInfo。该类还提供了一个ImageCellRenderer,该类用于将列表项绘制出用户图标和用户名字。

程序清单:codes\17\17.4\LanTalk\LanTalk.java

  1. public class LanTalk extends JFrame
  2. {
  3. private DefaultListModel<UserInfo> listModel
  4. = new DefaultListModel<>();
  5. // 定义一个JList对象
  6. private JList<UserInfo> friendsList = new JList<>(listModel);
  7. // 定义一个用于格式化日期的格式器
  8. private DateFormat formatter = DateFormat.getDateTimeInstance();
  9. public LanTalk()
  10. {
  11. super("局域网聊天");
  12. // 设置该JList使用ImageCellRenderer作为单元格绘制器
  13. friendsList.setCellRenderer(new ImageCellRenderer());
  14. listModel.addElement(new UserInfo("all" , "所有人"
  15. , null , -2000));
  16. friendsList.addMouseListener(new ChangeMusicListener());
  17. add(new JScrollPane(friendsList));
  18. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  19. setBounds(2, 2, 160 , 600);
  20. }
  21. // 根据地址来查询用户
  22. public UserInfo getUserBySocketAddress(SocketAddress address)
  23. {
  24. for (int i = 1 ; i < getUserNum() ; i++)
  25. {
  26. UserInfo user = getUser(i);
  27. if (user.getAddress() != null
  28. && user.getAddress().equals(address))
  29. {
  30. return user;
  31. }
  32. }
  33. return null;
  34. }
  35. // ------下面四个方法是对ListModel的包装------
  36. // 向用户列表中添加用户
  37. public void addUser(UserInfo user)
  38. {
  39. listModel.addElement(user);
  40. }
  41. // 从用户列表中删除用户
  42. public void removeUser(int pos)
  43. {
  44. listModel.removeElementAt(pos);
  45. }
  46. // 获取该聊天窗口的用户数量
  47. public int getUserNum()
  48. {
  49. return listModel.size();
  50. }
  51. // 获取指定位置的用户
  52. public UserInfo getUser(int pos)
  53. {
  54. return listModel.elementAt(pos);
  55. }
  56. // 实现JList上的鼠标双击事件监听器
  57. class ChangeMusicListener extends MouseAdapter
  58. {
  59. public void mouseClicked(MouseEvent e)
  60. {
  61. // 如果鼠标的击键次数大于2
  62. if (e.getClickCount() >= 2)
  63. {
  64. // 取出鼠标双击时选中的列表项
  65. UserInfo user = (UserInfo)friendsList.getSelectedValue();
  66. // 如果该列表项对应用户的交谈窗口为null
  67. if (user.getChatFrame() == null)
  68. {
  69. // 为该用户创建一个交谈窗口,并让该用户引用该窗口
  70. user.setChatFrame(new ChatFrame(null , user));
  71. }
  72. // 如果该用户的窗口没有显示,则让该用户的窗口显示出来
  73. if (!user.getChatFrame().isShowing())
  74. {
  75. user.getChatFrame().setVisible(true);
  76. }
  77. }
  78. }
  79. }
  80. /**
  81. * 处理网络数据报,该方法将根据聊天信息得到聊天者
  82. * 并将信息显示在聊天对话框中
  83. * @param packet 需要处理的数据报
  84. * @param single 该信息是否为私聊信息
  85. */
  86. public void processMsg(DatagramPacket packet , boolean single)
  87. {
  88. // 获取发送该数据报的SocketAddress
  89. InetSocketAddress srcAddress = (InetSocketAddress)
  90. packet.getSocketAddress();
  91. // 如果是私聊信息,则该Packet获取的是DatagramSocket的地址
  92. // 将端口号减1才是对应的MulticastSocket的地址
  93. if (single)
  94. {
  95. srcAddress = new InetSocketAddress(srcAddress.getHostName()
  96. , srcAddress.getPort() - 1);
  97. }
  98. UserInfo srcUser = getUserBySocketAddress(srcAddress);
  99. if (srcUser != null)
  100. {
  101. // 确定消息将要显示到哪个用户对应的窗口中
  102. UserInfo alertUser = single ? srcUser : getUser(0);
  103. // 如果该用户对应的窗口为空,则显示该窗口
  104. if (alertUser.getChatFrame() == null)
  105. {
  106. alertUser.setChatFrame(new ChatFrame(null , alertUser));
  107. }
  108. // 定义添加的提示信息
  109. String tipMsg = single ? "对您说:" : "对大家说:";
  110. // 显示提示信息
  111. alertUser.getChatFrame().addString(srcUser.getName()
  112. + tipMsg + "......................("
  113. + formatter.format(new Date()) + ")\n"
  114. + new String(packet.getData() , 0 , packet.getLength())
  115. + "\n");
  116. if (!alertUser.getChatFrame().isShowing())
  117. {
  118. alertUser.getChatFrame().setVisible(true);
  119. }
  120. }
  121. }
  122. // 主方法,程序的入口
  123. public static void main(String[] args)
  124. {
  125. LanTalk lanTalk = new LanTalk();
  126. new LoginFrame(lanTalk , "请输入用户名、头像后登录");
  127. }
  128. }
  129. // 定义用于改变JList列表项外观的类
  130. class ImageCellRenderer extends JPanel
  131. implements ListCellRenderer<UserInfo>
  132. {
  133. private ImageIcon icon;
  134. private String name;
  135. // 定义绘制单元格时的背景色
  136. private Color background;
  137. // 定义绘制单元格时的前景色
  138. private Color foreground;
  139. @Override
  140. public Component getListCellRendererComponent(JList list
  141. , UserInfo userInfo , int index
  142. , boolean isSelected , boolean cellHasFocus)
  143. {
  144. // 设置图标
  145. icon = new ImageIcon("ico/" + userInfo.getIcon() + ".gif");
  146. name = userInfo.getName();
  147. // 设置背景色、前景色
  148. background = isSelected ? list.getSelectionBackground()
  149. : list.getBackground();
  150. foreground = isSelected ? list.getSelectionForeground()
  151. : list.getForeground();
  152. // 返回该JPanel对象作为单元格绘制器
  153. return this;
  154. }
  155. // 重写paintComponent方法,改变JPanel的外观
  156. public void paintComponent(Graphics g)
  157. {
  158. int imageWidth = icon.getImage().getWidth(null);
  159. int imageHeight = icon.getImage().getHeight(null);
  160. g.setColor(background);
  161. g.fillRect(0, 0, getWidth(), getHeight());
  162. g.setColor(foreground);
  163. // 绘制好友图标
  164. g.drawImage(icon.getImage() , getWidth() / 2 - imageWidth / 2
  165. , 10 , null);
  166. g.setFont(new Font("SansSerif" , Font.BOLD , 18));
  167. // 绘制好友用户名
  168. g.drawString(name, getWidth() / 2 - name.length() * 10
  169. , imageHeight + 30 );
  170. }
  171. // 通过该方法来设置该ImageCellRenderer的最佳大小
  172. public Dimension getPreferredSize()
  173. {
  174. return new Dimension(60, 80);
  175. }
  176. }

上面类中提供的addUser()和removeUser()方法暴露给通信类ComUtil使用,用于向用户列表中添加、删除用户。除此之外,该类还提供了一个processMsg()方法,该方法用于处理网络中读取的数据报,将数据报中的内容取出,并显示在特定的窗口中。

上面讲解的只是本程序的关键类,本程序还涉及YeekuProtocol、ChatFrame、LoginFrame等类,由于篇幅关系,此处不再给出这些类的源代码,读者可以参考codes\17\17.4\LanTalk路径下的源代码。

17.4.3 使用MulticastSocket实现多点广播(5)的更多相关文章

  1. 17.4.3 使用MulticastSocket实现多点广播(4)

    17.4.3  使用MulticastSocket实现多点广播(4) 通过UserInfo类的封装,所有客户端只需要维护该UserInfo类的列表,程序就可以实现广播.发送私聊信息等功能.本程序底层通 ...

  2. 17.4.3 使用MulticastSocket实现多点广播(1)

    http://book.51cto.com/art/201203/322560.htm <疯狂Java讲义(第2版)>本书深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法 ...

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

    上面程序中init()方法里的第一行粗体字代码先创建了一个MulticastSocket对象,由于需要使用该对象接收数据报,所以为该Socket对象设置使用固定端口:第二行粗体字代码将该Socket对 ...

  4. 17.4.3 使用MulticastSocket实现多点广播(2)

    // 让该类实现Runnable接口,该类的实例可作为线程的target public class MulticastSocketTest implements Runnable { // 使用常量作 ...

  5. 使用MulticastSocket实现多点广播

    原文链接:http://hbiao68.iteye.com/blog/1943354 使用MulticastSocket实现多点广播 DatagramSocket只允许数据报发送给指定的目标地址,而M ...

  6. JAVA基础知识之网络编程——-使用MutilcastSocket实现多点广播

    IP多点广播原理 设置一组特殊网络地址作为多点广播地址,每一个多点广播地址都被看作一个组,当客户需要发送和接受信息时,加入到该组即可. IP协议为多点广播提供了一批特殊的IP地址,范围是224.0.0 ...

  7. java之DatagramSocket、DatagramPackage丶MulticastSocket 广播学习

    1.基本概念: a.DatagramPacket与DatagramSocket位于java.net包中 b.DatagramPacket表示存放数据的数据报,DatagramSocket表示接受或发送 ...

  8. Android设备一对多录屏直播--(UDP组播连接,Tcp传输)

    原文:https://blog.csdn.net/sunmmer123/article/details/82734245 近期需要学习流媒体知识,做一个Android设备相互投屏Demo,因此找到了这 ...

  9. Android开发之无线遥控器

    最近弄了一个UDP/TCP的小东西,主要需要实现的功能如下(服务器端): 1.基于局域网 2.服务器端网络接口为无线与有线 3.服务器端接收到客户端的数据需要模拟按键进行处理 4.开机自启动 5.使用 ...

随机推荐

  1. 经验分享:Xcode 创建.a和framework静态库

    最近因为项目中的聊天SDK,需要封装成静态库,所以实践了一下创建静态库的步骤,做下记录. 库介绍 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.库分静态库和动态库两种. iOS中的 ...

  2. Stack-overflow, how to answer

    How to Answer Welcome to Stack Overflow! Thanks for taking the time to contribute an answer. It's be ...

  3. OpenGL中shader读取实现

    1.需要shader在OpenGL中工作,必须经过如下过程 2.代码实现 /********** * loadshader.h **********/ #pragma once #define _CR ...

  4. js设置全局变量ajax中赋值

    js设置全局变量,在ajax中给予赋值赋值不上问题解决方案 方案一. //在全局或某个需要的函数内设置Ajax异步为false,也就是同步. $.ajaxSetup({async : false}); ...

  5. Dominating Patterns

    Dominating Patterns Time Limit:3000MS   Memory Limit:Unknown   64bit IO Format:%lld & %llu Descr ...

  6. Goods transportation

    Goods transportation time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  7. Struts中的数据处理的三种方式

    Struts中的数据处理的三种方式: public class DataAction extends ActionSupport{ @Override public String execute() ...

  8. spark中groupByKey与reducByKey

    [译]避免使用GroupByKey Scala Spark 技术   by:leotse 原文:Avoid GroupByKey 译文 让我们来看两个wordcount的例子,一个使用了reduceB ...

  9. CodeForces 567B Berland National Library

    Description Berland National Library has recently been built in the capital of Berland. In addition, ...

  10. J2SE网络编程之 TCP与UDP

    1.什么是TCP TCP(Transmission Control Protocol传输控制协议)是一种面向连接的.可靠的.基于字节流的通信协议,位于传输层.这三个特点中,面向连接就如同打电话,双方的 ...