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


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

    通过UserInfo类的封装,所有客户端只需要维护该UserInfo类的列表,程序就可以实现广播、发送私聊信息等功能。本程序底层通信的工具类则需要一个MulticastSocket和一个DatagramSocket,该工具类的代码如下。

    程序清单:codes1717.4LanTalkComUtil.java

    1. // 聊天交换信息的工具类
    2. public class ComUtil
    3. {
    4. // 使用常量作为本程序的多点广播IP地址
    5. private static final String BROADCAST_IP
    6. = "230.0.0.1";
    7. // 使用常量作为本程序的多点广播目的地端口
    8. // DatagramSocket所用的端口为该端口号-1
    9. public static final int BROADCAST_PORT = 30000;
    10. // 定义每个数据报的最大大小为4KB
    11. private static final int DATA_LEN = 4096;
    12. // 定义本程序的MulticastSocket实例
    13. private MulticastSocket socket = null;
    14. // 定义本程序私聊的Socket实例
    15. private DatagramSocket singleSocket = null;
    16. // 定义广播的IP地址
    17. private InetAddress broadcastAddress = null;
    18. // 定义接收网络数据的字节数组
    19. byte[] inBuff = new byte[DATA_LEN];
    20. // 以指定字节数组创建准备接收数据的DatagramPacket对象
    21. private DatagramPacket inPacket =
    22. new DatagramPacket(inBuff , inBuff.length);
    23. // 定义一个用于发送的DatagramPacket对象
    24. private DatagramPacket outPacket = null;
    25. // 聊天的主界面程序
    26. private LanTalk lanTalk;
    27. // 构造器,初始化资源
    28. public ComUtil(LanTalk lanTalk) throws Exception
    29. {
    30. this.lanTalk = lanTalk;
    31. // 创建用于发送、接收数据的MulticastSocket对象
    32. // 因为该MulticastSocket对象需要接收数据,所以有指定端口
    33. socket = new MulticastSocket(BROADCAST_PORT);
    34. // 创建私聊用的DatagramSocket对象
    35. singleSocket = new DatagramSocket(BROADCAST_PORT + 1);
    36. broadcastAddress = InetAddress.getByName(BROADCAST_IP);
    37. // 将该socket加入指定的多点广播地址
    38. socket.joinGroup(broadcastAddress);
    39. // 设置本MulticastSocket发送的数据报被回送到自身
    40. socket.setLoopbackMode(false);
    41. // 初始化发送用的DatagramSocket,它包含一个长度为0的字节数组
    42. outPacket = new DatagramPacket(new byte[0]
    43. , 0 , broadcastAddress , BROADCAST_PORT);
    44. // 启动两个读取网络数据的线程
    45. new ReadBroad().start();
    46. Thread.sleep(1);
    47. new ReadSingle().start();
    48. }
    49. // 广播消息的工具方法
    50. public void broadCast(String msg)
    51. {
    52. try
    53. {
    54. // 将msg字符串转换成字节数组
    55. byte[] buff = msg.getBytes();
    56. // 设置发送用的DatagramPacket里的字节数据
    57. outPacket.setData(buff);
    58. // 发送数据报
    59. socket.send(outPacket);
    60. }
    61. // 捕获异常
    62. catch (IOException ex)
    63. {
    64. ex.printStackTrace();
    65. if (socket != null)
    66. {
    67. // 关闭该Socket对象
    68. socket.close();
    69. }
    70. JOptionPane.showMessageDialog(null
    71. , "发送信息异常,请确认30000端口空闲,且网络连接正常!"
    72. , "网络异常", JOptionPane.ERROR_MESSAGE);
    73. System.exit(1);
    74. }
    75. }
    76. // 定义向单独用户发送消息的方法
    77. public void sendSingle(String msg , SocketAddress dest)
    78. {
    79. try
    80. {
    81. // 将msg字符串转换成字节数组
    82. byte[] buff = msg.getBytes();
    83. DatagramPacket packet = new DatagramPacket(buff
    84. , buff.length , dest);
    85. singleSocket.send(packet);
    86. }
    87. // 捕获异常
    88. catch (IOException ex)
    89. {
    90. ex.printStackTrace();
    91. if (singleSocket != null)
    92. {
    93. // 关闭该Socket对象
    94. singleSocket.close();
    95. }
    96. JOptionPane.showMessageDialog(null
    97. , "发送信息异常,请确认30001端口空闲,且网络连接正常!"
    98. , "网络异常", JOptionPane.ERROR_MESSAGE);
    99. System.exit(1);
    100. }
    101. }
    102. // 不断地从DatagramSocket中读取数据的线程
    103. class ReadSingle extends Thread
    104. {
    105. // 定义接收网络数据的字节数组
    106. byte[] singleBuff = new byte[DATA_LEN];
    107. private DatagramPacket singlePacket =
    108. new DatagramPacket(singleBuff , singleBuff.length);
    109. public void run()
    110. {
    111. while (true)
    112. {
    113. try
    114. {
    115. // 读取Socket中的数据
    116. singleSocket.receive(singlePacket);
    117. // 处理读到的信息
    118. lanTalk.processMsg(singlePacket , true);
    119. }
    120. // 捕获异常
    121. catch (IOException ex)
    122. {
    123. ex.printStackTrace();
    124. if (singleSocket != null)
    125. {
    126. // 关闭该Socket对象
    127. singleSocket.close();
    128. }
    129. JOptionPane.showMessageDialog(null
    130. , "接收信息异常,请确认30001端口空闲,且网络连接正常!"
    131. , "网络异常", JOptionPane.ERROR_MESSAGE);
    132. System.exit(1);
    133. }
    134. }
    135. }
    136. }
    137. // 持续读取MulticastSocket的线程
    138. class ReadBroad extends Thread
    139. {
    140. public void run()
    141. {
    142. while (true)
    143. {
    144. try
    145. {
    146. // 读取Socket中的数据
    147. socket.receive(inPacket);
    148. // 打印输出从Socket中读取的内容
    149. String msg = new String(inBuff , 0
    150. , inPacket.getLength());
    151. // 读到的内容是在线信息
    152. if (msg.startsWith(YeekuProtocol.PRESENCE)
    153. && msg.endsWith(YeekuProtocol.PRESENCE))
    154. {
    155. String userMsg = msg.substring(2
    156. , msg.length() - 2);
    157. String[] userInfo = userMsg.split(YeekuProtocol
    158. .SPLITTER);
    159. UserInfo user = new UserInfo(userInfo[1]
    160. , userInfo[0] , inPacket.getSocketAddress(), 0);
    161. // 控制是否需要添加该用户的旗标
    162. boolean addFlag = true;
    163. ArrayList<Integer> delList = new ArrayList<>();
    164. // 遍历系统中已有的所有用户,该循环必须循环完成
    165. for (int i = 1 ; i < lanTalk.getUserNum() ; i++ )
    166. {
    167. UserInfo current = lanTalk.getUser(i);
    168. // 将所有用户失去联系的次数加1
    169. current.setLost(current.getLost() + 1);
    170. // 如果该信息由指定用户发送
    171. if (current.equals(user))
    172. {
    173. current.setLost(0);
    174. // 设置该用户无须添加
    175. addFlag = false;
    176. }
    177. if (current.getLost() > 2)
    178. {
    179. delList.add(i);
    180. }
    181. }
    182. // 删除delList中的所有索引对应的用户
    183. for (int i = 0; i < delList.size() ; i++)
    184. {
    185. lanTalk.removeUser(delList.get(i));
    186. }
    187. if (addFlag)
    188. {
    189. // 添加新用户
    190. lanTalk.addUser(user);
    191. }
    192. }
    193. // 读到的内容是公聊信息
    194. else
    195. {
    196. // 处理读到的信息
    197. lanTalk.processMsg(inPacket , false);
    198. }
    199. }
    200. // 捕获异常
    201. catch (IOException ex)
    202. {
    203. ex.printStackTrace();
    204. if (socket != null)
    205. {
    206. // 关闭该Socket对象
    207. socket.close();
    208. }
    209. JOptionPane.showMessageDialog(null
    210. , "接收信息异常,请确认30000端口空闲,且网络连接正常!"
    211. , "网络异常", JOptionPane.ERROR_MESSAGE);
    212. System.exit(1);
    213. }
    214. }
    215. }
    216. }
    217. }
  • 相关阅读:
    Matlab绘图基础——利用axes(坐标系图形对象)绘制重叠图像 及 一图多轴(一幅图绘制多个坐标轴)
    [学习笔记]Javaweb开发视频教程之Tomcat9配置
    Matlab绘图基础——axis设置坐标轴取值范围
    Cauchy-Binet公式的证明 及 对《来自特征值的特征向量》的理解
    [问题解决]win10误删启动项(BCD)(HP电脑亲测,无需启动盘,并非重装系统)
    [经验分享]用自相似的思想来理解二叉树的三种遍历方法
    [参考]用递归的方法获取 字符 对应的 二进制字符串 (C/C++)
    [经验分享]SecureCRT导出操作日志 + Notepad自定义语言格式高亮日志文件
    [公式推导]一般线性秩统计量的方差函数 及其 极限分布
    [问题解决]RedHat7更换CentOS7的yum源时踩过的坑
  • 原文地址:https://www.cnblogs.com/senior-engineer/p/4967216.html
Copyright © 2020-2023  润新知