Java利用UDP实现简单群聊

news/2024/5/18 11:52:39 标签: java, udp, python

一、创建新项目
首先新建一个新的项目,并按如下操作

二、实现代码
界面ChatFrame类
package 群聊;
 
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();//让发送文本框得到焦点
    }
}

组播聊天SocketChat类
package 群聊;
 
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);
    }
}

三、运行结果


运行的第一个窗口成员1

第二个窗口为成员2

第三个窗口为成员3

由此可见、新建窗口=添加成员


http://www.niftyadmin.cn/n/5242618.html

相关文章

Multidimensional Scaling(MDS多维缩放)算法及其应用

在这篇博客中&#xff0c;我将与大家分享在流形分析领域的一个非常重要的方法&#xff0c;即多维缩放MDS。整体来说&#xff0c;该方法提供了一种将内蕴距离映射到显性欧氏空间的计算&#xff0c;为非刚性形状分析提供了一种解决方案。当初就是因为读了Bronstein的相关工作【1】…

ArcGIS Enterprise on Kubernetes 11.1安装示例

博客主页&#xff1a;https://tomcat.blog.csdn.net 博主昵称&#xff1a;农民工老王 主要领域&#xff1a;Java、Linux、K8S 期待大家的关注&#x1f496;点赞&#x1f44d;收藏⭐留言&#x1f4ac; 目录 安装前置条件基本安装解压文件生成秘钥执行安装脚本 配置DNS方法一方法…

js实现AES加密解密,简易又全面

常规是直接安装CryptoJS库&#xff0c;但为了减少项目体积&#xff0c;使用这简单的20k文件就ok 一览&#xff1a; 代码中使用的是Pkcs7&#xff0c;但我需要的填充方式是ZeroPadding 所以稍微有修改&#xff1a; q (p.pad {}).ZeroPadding {pad: function (data, blockSi…

JavaScript中处理时间差

ES6版本 function countdown(endTime, includeSeconds true) {// 获取当前时间let now new Date();// 将传入的结束时间字符串转换为日期对象let endDateTime new Date(endTime);// 检查传入的时间字符串是否只包含日期&#xff08;不包含时分秒&#xff09;if (endTime.tr…

甘草书店:#8 2023年11月22日 星期三「“说一套做一套”的甘草与麦田」

最近与甘草书店的投资方和意向投资方沟通&#xff0c;听取了来自不同领域不同人群的观点。他们讲的都有道理&#xff0c;但他们说的都不是甘草。就像“麦田”成立之前&#xff0c;世间没有“麦田”一样&#xff1b;“甘草”出现之前&#xff0c;世间没有也没有“甘草”。 故事…

Python (二) 读写excel文件

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

统计英语单词

使用Scanner类和正则表达式统计一篇英文中的单词&#xff0c;要求如下&#xff1a; 1、一共出现了多少个单词。 2、有多少个互不相同的单词。 3、按单词出现的频率大小输出单词。 package 第七次; import java.util.*; import java.io.*; import java.util.regex.*;public …

java中AQS使用双向链表的原因是什么?

AQS&#xff08;AbstractQueuedSynchronizer&#xff09;使用双向链表的主要原因是为了实现等待队列&#xff08;Wait Queue&#xff09;。这个等待队列是用于管理在同步器上等待的线程&#xff0c;例如在获取锁时发现锁被占用&#xff0c;线程就会加入到等待队列中等待。 使用…