如何利用UDP实现群聊聊天室?

大家好,今天为大家带来了一个非常有意思的小程序——UDP实现的群聊聊天室。这个程序使用的UDP协议,并使用DatagramSocket的子类MulticastSocket实现组播,可以部署在一个局域网内的多台电脑上,并可以实现文字群聊。

本文将会按照以下几个小节讲解:

1). 组播的概念:这个小节我们将讲解什么是:单播、广播、组播。

2). MulticastSocket类的使用:这个小节我们将讲解MulticastSocket类的基本使用,并实现控制台的信息收发。

3). 基于Swing和MulticastSocket实现的UDP群聊聊天室:这个小节我们将制作一个界面,并结合MulticastSocket类实现一个完整的UDP群聊聊天室。

4). 结束语:

一、组播的概念

网络数据传播按照接收者的数量,可分为以下3种方式:

1.1 单播:

单播是指实现“点对点”的通信,发送者发送数据要发送给网络上的唯一的一台电脑,指定一个接收者。像TCP协议和UDP协议都能实现点对点通信。

1.2 广播:

发送者发送的数据可以被某个接收范围内所有的接收者接收。它类似于广播电台,向某个范围内的所有用户发送广播信号,接收人打开广播就可以听到,关闭广播设备就停止收听。由于广播会大大增加网络数据流量,所以通常情况下一些网络路由器会禁止广播数据,尤其是一些占用网络资源比较大的视频数据等。

1.3 组播:

组播是指发送的数据可以被指定的一组用户接收。组播的范围没有广播那么广,任何的一台电脑都可以随时加入某一个组接收组播数据。若要使用组播,则需要让一个数据报标有一组目标主机地址,当数据报发出后,整个组的所有主机都能收到该数据报。IP协议为组播提供了这批特殊的IP地址,这些IP地址的范围是224.0.0.0至239.255.255.255。在Java类库中,DatagramSocket有一个子类:MulticastSocket,它具有组播的功能,它可以与DatagramPackage结合使用,用于发送和接收组播包。

二. MulticastSocket类的使用

Java类库中MulticastSocket类可以实现组播功能,它是DatagramSocket的子类:

如何利用UDP实现群聊聊天室?

2.1 构造方法说明

通过API文档我们可以看到它有三个构造方法:

1. MulticastSocket() 创建一个多播套接字。(使用随机端口,如果只发送,可以使用这个构造方法) 2. MulticastSocket(int port) 创建一个多播套接字并将其绑定到一个特定的端口。(如果需要发送和接收,需要使用这个构造方法)3. MulticastSocket(SocketAddress bindaddr) 创建一个多播套接字绑定到指定的套接字地址。 

2.2 成员方法说明

以下是几个比较重要的成员方法:

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。

2.3 一个简单的示例

接下来我们写一个小例子来看一下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 {        //创建MuticastSocket对象,并监听端口55555        MulticastSocket socket = new MulticastSocket(55555);        //加入组:235.235.235.235        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();        //获取本机IP        String localIp = InetAddress.getLocalHost().getHostAddress();        //创建一个Scanner对象,用于接收控制台数据        Scanner sc = new Scanner(System.in);        while (true) {            System.out.println("【请输入信息】");            String msg = sc.next();            // 获取当前时间格式化字符串,把IP、时间,以及要发送的文本连接在一起            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和MulticastSocket实现的UDP群聊聊天室

接下来我们使用Swing为这个程序制作一个界面,让用户操作起来更加方便。

这个程序我们制作了两个类:

1). ChatFrame:这个类继承自JFrame,实现了界面的显示、布局等相关功能。

2). SocketChat:这个类继承自ChatFrame,加入了MulticastSocket的连接、信息发送和接收。

3.1 界面ChatFrame类

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() {        // 给发送按键添加监听器,当被点击时调用send()方法        sendBtn.addActionListener(new ActionListener() {            public void actionPerformed(ActionEvent evt) {                send();            }        });        // 给发送文本框添加键盘监听器,当按下Ctrl+ENTER时调用send()方法        sendArea.addKeyListener(new KeyAdapter() {            public void keyPressed(KeyEvent e) {                if(e.isControlDown()) {                    if(e.getKeyCode() == KeyEvent.VK_ENTER) {                        send();                    }                }            }        });    }    // 子类需要重写本方法    // 在本方法中使用socket实现消息发送    public abstract void sendText(String text);    // 子类需要重写本方法    // 在本方法中启动监听服务器线程,调用本类receiveText(String)把接收到的文本显示出来    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);    }    // 本方法完成接收服务器消息的后续工作-在文本框中显示服务器消息,子类的receive()方法在接收服务器消息后可以调用本方法    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);        // 设置主窗口的标题为当前IP地址        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);        // 设置主窗口的“X”按钮点击后结束程序        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    }    // 显示主窗口方法    public void setVisible(boolean b) {        super.setVisible(b);//调用父类的显示方法        sendArea.requestFocus();//让发送文本框得到焦点    }}

这个类中定义了很多抽象方法,这些抽象方法由子类实现。

3.2 组播聊天SocketChat类

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端口        //加入虚拟IP:235.235.235.235指定的群组中。虚拟IP范围是:224.0.0.1 和 239.255.255.255        //加入群组后,就可以接收群组的消息,也可以向群组发送消息了        socket.joinGroup(InetAddress.getByName("235.235.235.235"));    }    // 发送消息方法    public void sendText(String text) {        try {            // 获取IP地址            String ip = InetAddress.getLocalHost().getHostAddress();            // 获取当前时间格式化字符串            String time = String.format(" <====> %tF %<tT", new Date());            // 把IP、时间,以及要发送的文本连接在一起            text = ip + time + "\\n" + text + "\\n\\n";            // 把文本转换成字节数组            byte[] buff = text.getBytes();            // 使用socket向群组发送,socket的send()方法需要两个参数:DatagramPacket、端口号            // DatagramPacket表示数据包,创建它需要三个参数:数据包的内容、数据包的字节数、要发送的IP地址            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 {                        // 创建数据包的字节数组,大小为1KB                        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类的使用,有兴趣的朋友可以基于这个程序,为它添加更多的功能,例如:文件的发送、接收;表情图片的发送、接收等等。后面我还会为大家带来更多更实用的程序,期待大家来围观哦!谢谢大家!!

本文来自投稿,不代表重蔚自留地立场,如若转载,请注明出处https://www.cwhello.com/262743.html

如有侵犯您的合法权益请发邮件951076433@qq.com联系删除

(0)
黑马程序员黑马程序员订阅用户
上一篇 2023年5月12日 10:24
下一篇 2023年5月12日

相关推荐

  • 什么是OCR?OCR技术详解

    光学字符识别(Optical Character Recognition)简称为“OCR”。ORC是指对包含文本资料的图像文件进行分析识别处理,获取文字及版面信息的技术。一般包括以下几个过程:1.图像输入针对不同格式的图像,有着不同的存储格…

    2023年8月12日
    012
  • 小编教你快手生意通有哪些功能。

    模块通用:Q1:生意通的各个指标定义是什么?A1:详细指标可查看相关定义文档,随着产品迭代,相关指标将不断新增与迭代:快手生意通指标定义(对外):快手生意通-商品模块改版上线了,但是肯定也有不少快手卖家不太…

    2023年11月5日
    00
  • 今日分享vps怎么开启udp端口。

    要在VPS上开启UDP端口,您需要进行以下步骤:1. 登录到VPS的控制台或通过SSH连接到VPS的终端。2. 打开VPS的防火墙配置文件,通常是/etc/iptables/rules.v4或/etc/sysconfig/iptables。3. 在文件中找到类似以下内容…

    2024年7月5日
    00
  • 小编分享如何设计更好的功能页面。

    功能页面的设计是网站建设或者网站升级、改造的难点之一。做好信息架构设计后,您的网站将拥有干净,有条理的感觉,可以更好地代表您的品牌。网站将如何发挥其作用,其功能页面如何吸引用户?小编科技作为高端网站设…

    2023年6月16日
    00
  • 教你iphone 如何呼叫转移。

    在iPhone上设置呼叫转移,请打开“设置”应用,选择“电话”,点击“呼叫转移”,然后根据需要选择转移类型并输入要转移的号码。 (图片来源网络,侵删) 方法一:通过iPhone自带功能设置呼叫转移 1、打开“设置”应用,在…

    2024年6月27日
    00
  • javascript实现下拉框与复选框操作教程

    下拉框操作 实现效果如下: 完整源码如下:   无标题文档 ==请选择== ==红色== ==绿色== ==蓝色== 以上就是通过js实现下拉框与复选框的操作方法

    2018年4月2日
    0361
  • spring框架怎么实现依赖注入?

    依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入。具体介绍如下:● 属性setter方法注入:指IoC容…

    2023年5月15日
    011
  • 小编分享网站建设中有哪些功能是必不可少的。

    一个网站的最终目的是为用户服务还是为企业服务的,或者二者之间兼而有之,网站在方便用户的同时,也让自己获得利益。在网站建设中,哪些功能必须需要的呢?下面小编就来告诉大家。 谈到网站功能,包括的范围很多,…

    2023年6月12日
    00

联系我们

QQ:951076433

在线咨询:点击这里给我发消息邮件:951076433@qq.com工作时间:周一至周五,9:30-18:30,节假日休息