该类主要实现底层的网络通信功能,在该类中提供了一个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. ubuntu 自动获取ip的怎么设置

    ubuntu以DHCP方式配置网卡自动获取ip编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces并用下面的行来替换有关eth0的行: ...

  2. Qt中利用QTime类来控制时间,这里简单介绍一下QTime的成员函数的用法:

    Qt中利用QTime类来控制时间,这里简单介绍一下QTime的成员函数的用法: ------------------------------------------------------------ ...

  3. 【转】Xshell 十个技巧

    原文:http://www.cnblogs.com/wanhl/archive/2012/10/17/2727607.html 一.帐号密码保存.可以保存多个vps登陆信息,免去每次输入的烦恼. 二. ...

  4. Vs2010 WPF 项目打包

    [转]图解WPF程序打包全过程 首先打开已经完成的工程,如图: 下面开始制作安装程序包. 第一步:[文件]——[新建]——[项目]——安装项目. 名称——可以自己根据要求修改. 位置——是指你要制作的 ...

  5. HDU 2802 F(N) 数论+打表

    题目大意:f[n]-n^3=f[n-2]-(n-1)^3 (n >=3),f[1]=1,f[2]=7,求f[n]. 题目思路:将n^3移到到等式右边化简的到:f[n]=f[n-2]+3n*(n- ...

  6. struts2+ajax实现异步验证实现

    由于老师布置作业的需要,在添加管理员的时候,要实现验证添加的管理员的用户名是否在数据库中已经存在,然后再客户端给用户一个提示.我首先想到的就是利用ajax实现异步验证技术,由于利用的ssh框架,所以在 ...

  7. Git学习 -- 简介

    Git是什么? 是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. 特点 分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本 ...

  8. Oracle Sql优化之分层查询(connect by)

    1.对于表中行与行存在父子关系时,可以通过connect by查询方式,查询行与行之间的父子关系 ),'-')|| empno as tempno, ename,mgr,level, decode(, ...

  9. 转:【WebDriver】封装GET方法来解决页面跳转不稳定的问题

    在大多数测试环境中,网络或者测试服务器主机之间并不是永远不出问题的,很多时候一个客户端的一个跳转的请求会因为不稳定的网络或者偶发的其它异常hang死在那里半天不动,直到人工干预动作的出现.      ...

  10. LoadRunner监控Unix、Windows方法及常用性能指标

    目  录 一.LoadRunner监控Linux资源.... 3 (一).准备工作... 3 1.可以通过两种方法验证服务器上是否配置了rstatd守护程序:... 3 (2)使用find命令... ...