更新时间:2022年11月15日14时27分 来源:传智教育 浏览次数:
大家好,今天为大家带来了一个非常有意思的小程序——UDP实现的群聊聊天室。这个程序使用的UDP协议,并使用DatagramSocket的子类MulticastSocket实现组播,可以部署在一个局域网内的多台电脑上,并可以实现文字群聊。
本文将会按照以下几个小节讲解:
1). 组播的概念:**这个小节我们将讲解什么是:单播、广播、组播。
2). MulticastSocket类的使用:**这个小节我们将讲解MulticastSocket类的基本使用,并实现控制台的信息收发。
3). 基于Swing和MulticastSocket实现的UDP群聊聊天室:**这个小节我们将制作一个界面,并结合MulticastSocket类实现一个完整的UDP群聊聊天室。
4). 结束语:
网络数据传播按照接收者的数量,可分为以下3种方式:
单播是指实现“点对点”的通信,发送者发送数据要发送给网络上的唯一的一台电脑,指定一个接收者。像TCP协议和UDP协议都能实现点对点通信。
发送者发送的数据可以被某个接收范围内所有的接收者接收。它类似于广播电台,向某个范围内的所有用户发送广播信号,接收人打开广播就可以听到,关闭广播设备就停止收听。由于广播会大大增加网络数据流量,所以通常情况下一些网络路由器会禁止广播数据,尤其是一些占用网络资源比较大的视频数据等。
组播是指发送的数据可以被指定的一组用户接收。组播的范围没有广播那么广,任何的一台电脑都可以随时加入某一个组接收组播数据。若要使用组播,则需要让一个数据报标有一组目标主机地址,当数据报发出后,整个组的所有主机都能收到该数据报。IP协议为组播提供了这批特殊的IP地址,这些IP地址的范围是224.0.0.0至239.255.255.255。在Java类库中,DatagramSocket有一个子类:MulticastSocket,它具有组播的功能,它可以与DatagramPackage结合使用,用于发送和接收组播包。
Java类库中MulticastSocket类可以实现组播功能,它是DatagramSocket的子类:
通过API文档我们可以看到它有三个构造方法:
1. MulticastSocket() 创建一个多播套接字。(使用随机端口,如果只发送,可以使用这个构造方法) 2. MulticastSocket(int port) 创建一个多播套接字并将其绑定到一个特定的端口。(如果需要发送和接收,需要使用这个构造方法) 3. MulticastSocket(SocketAddress bindaddr) 创建一个多播套接字绑定到指定的套接字地址。
以下是几个比较重要的成员方法:
1.public void joinGroup(InetAddress mcastaddr):将该MulticastSocket加入指定的多点广播地址。 2.public void leaveGroup(InetAddress mcastaddr让该MulticastSocket离开指定的多点广播地址。 3.public void setInterface(InetAddress inf):如果当前系统有多个网络接口,可以使用次方法指定一个网络接口。 4.public InetAddress getInterface():获取当前的网络接口。 5.public void setTimeToLive(int ttl):该参数设置数据报最多可以跨过多少个网络,当ttl为0时,指定数据报应停留在本地主机;当ttl的值为1时,指定数据报发送到本地局域网;当ttl的值为32时,意味着只能发送到本站点的网络上;当ttl为64时,意味着数据报应保留在本地区;当ttl的值为128时,意味着数据报应保留在本大洲;当ttl为255时,意味着数据报可发送到所有地方;默认情况下,该ttl的值为1。
接下来我们写一个小例子来看一下MulticastSocket的使用方式。这个程序将包含两个线程:1. 接收线程,主要用于接收信息;2. 主线程,主要用于发送信息。将这个程序部署到局域网上的几台电脑上,全部启动,就可以实现多台电脑的组播了,而且每台主机都可以发出信息,其它主机则会收到这条信息。
package com.heima.se.chat; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.util.Date; import java.util.Scanner; public class MulticastSocketDemo { public static void main(String[] args) throws IOException { MulticastSocket socket = new MulticastSocket(55555); socket.joinGroup(InetAddress.getByName("235.235.235.235")); new Thread(()->{ byte[] bytes = new byte[1024]; DatagramPacket packet = new DatagramPacket(bytes, bytes.length); while (true) { try { socket.receive(packet); System.out.println(new String(packet.getData(), 0, packet.getLength())); } catch (IOException e) { e.printStackTrace(); } } }).start(); String localIp = InetAddress.getLocalHost().getHostAddress(); Scanner sc = new Scanner(System.in); while (true) { System.out.println("【请输入信息】"); String msg = sc.next(); String time = String.format(" <====> %tF %<tT", new Date()); msg = localIp + time + "\n" + msg + "\n\n"; socket.send(new DatagramPacket(msg.getBytes(), msg.getBytes().length, InetAddress.getByName("235.235.235.235"), 55555)); } } }
通过上面的程序,我们发现,MulticastSocket类的使用和DatagramSocket类基本相同,只是多了一步加入组:joinGroup(),所有加入这个组的主机都将会收到信息。
接下来我们使用Swing为这个程序制作一个界面,让用户操作起来更加方便。
这个程序我们制作了两个类:
1). ChatFrame:这个类继承自JFrame,实现了界面的显示、布局等相关功能。
2). SocketChat:这个类继承自ChatFrame,加入了MulticastSocket的连接、信息发送和接收。
package com.heima.se.chat; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.net.InetAddress; public abstract class ChatFrame extends JFrame { private JTextArea receiveArea = new JTextArea();//接收文本框,用来显示服务器发送过来的文本 private JTextArea sendArea = new JTextArea();//发送文本框,用来显示当前用户要发送的文本 private JButton sendBtn = new JButton("SEND");//发送按键 public ChatFrame() { this.initFrame();//初始化窗口 this.initComponent();//初始化组件 this.initListener();//初始化监听器 this.receive();//开启监听服务器线程,把接收到的文本显示在receiveArea中 } private void initListener() { sendBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { send(); } }); sendArea.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if(e.isControlDown()) { if(e.getKeyCode() == KeyEvent.VK_ENTER) { send(); } } } }); } public abstract void sendText(String text); public abstract void receive(); public void send() { if(sendArea.getText().equals("")) { javax.swing.JOptionPane.showMessageDialog(this, "空文本不能发送!"); sendArea.requestFocus();// 把光标归还给发送文本框 return; } sendText(sendArea.getText()); sendArea.setText(null); } public void receiveText(String text) { receiveArea.append(text);//把接收到的消息添加到文本框中 receiveArea.setCaretPosition(receiveArea.getText().length()); } private void initComponent() { JScrollPane sp1 = new JScrollPane(receiveArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); sp1.setSize(606, 350); sp1.setLocation(14, 20); sp1.setBorder(null); this.add(sp1); receiveArea.setBackground(new Color(238, 238, 238)); receiveArea.setEditable(false); receiveArea.setLineWrap(true); JScrollPane sp2 = new JScrollPane(sendArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); sendArea.setLineWrap(true); sp2.setSize(606, 145); sp2.setLocation(14, 400); this.add(sp2); sendBtn.setSize(68, 21); sendBtn.setLocation(553, 560); this.add(sendBtn); try { this.setTitle(InetAddress.getLocalHost().getHostAddress()); } catch (Exception e) { throw new RuntimeException(e); } } private void initFrame() { this.setSize(640, 620); this.setLayout(null); this.setBackground(new Color(246, 246, 247)); this.setLocation(350, 50); this.setResizable(false); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void setVisible(boolean b) { super.setVisible(b);//调用父类的显示方法 sendArea.requestFocus();//让发送文本框得到焦点 } }
这个类中定义了很多抽象方法,这些抽象方法由子类实现。
package com.heima.se.chat; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.util.Date; /** * 本类继承了ChatFrame,ChatFrame实现了GUI显示 * 本类负责使用MulticastSocket完成群聊的发送消息与接收消息 */ public class SocketChat extends ChatFrame { private MulticastSocket socket;//群组Socket public SocketChat() throws IOException { socket = new MulticastSocket(54321);//创建群组Socket,绑定54321端口 socket.joinGroup(InetAddress.getByName("235.235.235.235")); } public void sendText(String text) { try { String ip = InetAddress.getLocalHost().getHostAddress(); String time = String.format(" <====> %tF %<tT", new Date()); text = ip + time + "\n" + text + "\n\n"; byte[] buff = text.getBytes(); socket.send(new DatagramPacket(buff, buff.length, InetAddress.getByName("235.235.235.235"), 54321)); } catch(Exception e) { e.printStackTrace(); } } public void receive() { new Thread() { public void run() { while(true) { try { byte[] buff = new byte[1024]; DatagramPacket dp = new DatagramPacket(buff, buff.length); socket.receive(dp); String text = new String(dp.getData(), 0, dp.getLength()); receiveText(text); } catch(Exception e) {} } } }.start(); } public static void main(String[] args) throws IOException { SocketChat sc = new SocketChat(); sc.setVisible(true); } }
这个类使用MulticastSocket,使用端口:54321,组播地址:235.235.235.235。当用户在界面按下send按钮时,会触发sendText()方法发送数据;receive()方法用于使用线程接收数据,它是在父类的构造方法中被触发启动,启动后,使用无限循环进行信息的接收。
这篇文章我们使用MulticastSocket类实现了组播功能,并使用Swing和MulticastSocket制作了一个基于UDP的群聊聊天室,希望大家能够通过本篇文章了解MulticastSocket类的使用,有兴趣的朋友可以基于这个程序,为它添加更多的功能,例如:文件的发送、接收;表情图片的发送、接收等等。后面我还会为大家带来更多更实用的程序,期待大家来围观哦!谢谢大家!!