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<UserInfo> listModel
- = new DefaultListModel<>();
- // 定义一个JList对象
- private JList<UserInfo> 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<UserInfo>
- {
- 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路径下的源代码。
17.4.3 使用MulticastSocket实现多点广播(5)的更多相关文章
- 17.4.3 使用MulticastSocket实现多点广播(4)
17.4.3 使用MulticastSocket实现多点广播(4) 通过UserInfo类的封装,所有客户端只需要维护该UserInfo类的列表,程序就可以实现广播.发送私聊信息等功能.本程序底层通 ...
- 17.4.3 使用MulticastSocket实现多点广播(1)
http://book.51cto.com/art/201203/322560.htm <疯狂Java讲义(第2版)>本书深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法 ...
- 17.4.3 使用MulticastSocket实现多点广播(3)
上面程序中init()方法里的第一行粗体字代码先创建了一个MulticastSocket对象,由于需要使用该对象接收数据报,所以为该Socket对象设置使用固定端口:第二行粗体字代码将该Socket对 ...
- 17.4.3 使用MulticastSocket实现多点广播(2)
// 让该类实现Runnable接口,该类的实例可作为线程的target public class MulticastSocketTest implements Runnable { // 使用常量作 ...
- 使用MulticastSocket实现多点广播
原文链接:http://hbiao68.iteye.com/blog/1943354 使用MulticastSocket实现多点广播 DatagramSocket只允许数据报发送给指定的目标地址,而M ...
- JAVA基础知识之网络编程——-使用MutilcastSocket实现多点广播
IP多点广播原理 设置一组特殊网络地址作为多点广播地址,每一个多点广播地址都被看作一个组,当客户需要发送和接受信息时,加入到该组即可. IP协议为多点广播提供了一批特殊的IP地址,范围是224.0.0 ...
- java之DatagramSocket、DatagramPackage丶MulticastSocket 广播学习
1.基本概念: a.DatagramPacket与DatagramSocket位于java.net包中 b.DatagramPacket表示存放数据的数据报,DatagramSocket表示接受或发送 ...
- Android设备一对多录屏直播--(UDP组播连接,Tcp传输)
原文:https://blog.csdn.net/sunmmer123/article/details/82734245 近期需要学习流媒体知识,做一个Android设备相互投屏Demo,因此找到了这 ...
- Android开发之无线遥控器
最近弄了一个UDP/TCP的小东西,主要需要实现的功能如下(服务器端): 1.基于局域网 2.服务器端网络接口为无线与有线 3.服务器端接收到客户端的数据需要模拟按键进行处理 4.开机自启动 5.使用 ...
随机推荐
- spring securiy使用总结
我们常见的几个功能: 注册后直接登录,并且remember-me这种在网上找到很多注册后登录的,但是remember-me没有.其实解决方案还是看源码比较方便.a. 装载authenticationM ...
- AVFoundation(二)---MPMusicPlayerController
话不多说,直接上代码,代码中详细介绍了如何初始化,并设置一些常用的属性. /** * MPMusicPlayerController用于播放音乐库中的音乐 */ /** * 初始化MPMusicPla ...
- 改良UIScrollView滚动视图
#define HEIGHT self.view.frame.size.height #define WIDTH self.view.frame.size.width @interface V ...
- C++矩阵处理库--Eigen初步使用
项目要进行比较多的矩阵操作,特别是二维矩阵.刚开始做实验时,使用了动态二维数组,于是写了一堆Matrix函数,作矩阵的乘除加减求逆求行列式.实验做完了,开始做代码优化,发现Matrix.h文件里适 ...
- execlp("ls","flw","-?",(char *)0) 为什么少了最后的一个参数就不行?
execlp("ls","flw","-?",(char *)0) 为什么少了最后的一个参数就不行?
- 从零开始学习OpenGL ES之一 – 基本概念
我曾写过一些文章介绍iPhone OpenGL ES编程,但大部分针对的是已经至少懂得一些3D编程知识的人.作为起点,请下载我的OpenGL Xcode项目模板,而不要使用Apple提供的模板.你可以 ...
- UIWebView 使用要注意的几点
UIWebView 使用要注意的几点 最近有客户希望将移动端统一使用HTML5来完成,在iOS端就要用到UIWebView.遇到了以下三个主要问题: 加载HTTPS页面 不像Safari可以弹出弹框问 ...
- PAT (Advanced Level) 1106. Lowest Price in Supply Chain (25)
简单dfs #include<cstdio> #include<cstring> #include<cmath> #include<vector> #i ...
- Mysql笔记3数据库基本操作
1 创建数据库 create database 数据名称 default character set 编码; 2查看常用的编码校验规则 mysql> show character set; 3删 ...
- Entity Framework 学习初级篇7--基本操作:增加、更新、删除、事务
本节,直接写通过代码来学习.这些基本操作都比较简单,与这些基本操作相关的内容在之前的1至6节基本介绍完毕. l 增加: 方法1:使用AddToXXX(xxx)方法:实例代码如下: ...