全国
Java--教育路上

栏目导航

全国站 > Java > 网络编程
需求

UDP 案例—聊天程序

摘要:Java UDP 案例 聊天 程序 ,UDP网络程序的基本过程。下面将多线程和Swing 的相关知识结合起来实现一个功能完善的聊天程序。。以下是我们为大家整理的,相信大家阅读完后肯定有了自己的选择吧。

标签:
发布时间:
2024-05-19 20:00
信息来源:
网络推荐
浏览次数:
1305
UDP 案例—聊天程序

通过上面的讲解,大家应该掌握了编写UDP网络程序的基本过程。下面将多线程和Swing 的相关知识结合起来实现一个功能完善的聊天程序。 在实现案例之前,首先分析一下功能需求。现在所要编写的 UDP 聊天程序主要是想通 过监听指定的端口号,目标 IP 地址和目标端口号,实现消息的发送和接收功能,并把聊天 内容显示出来。程序的用户图形界面如图 13-12 所示

from shengerguan


在实际开发中,为了使程序条理清晰,通常会将一个大问题分
决,这里为了使大家更容易理解,把聊天程序分为以下步骤来实现:
1.界面实现
对于界面的实现,首先定义一个类 GuiChat 继承自 JFram 类,类中声明成员变量,成员变
量包括组成用户图形界面的各个组件以及 DatagramSocket 等,如例 13-4 所示。
例 13-4 GuiChat.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.IOException;
import java.net.*;
public class GuiChat extends JFrame {
private static final int DEFAULT_PORT = 8899;
// 把主窗口分为 NORTH、CEMTER 和 SOUTH 三个部分
private JLabel stateLB; // 显示监听状态
private JTextArea centerTextArea; // 显示聊天记录
private JPanel southPanel; // 最下面的面板
private JTextArea inputTextArea; // 聊天输入框
private JPanel bottomPanel; // 放置 IP 输入框,按钮等
private JTextField ipTextField; // IP 输入框
private JTextField remotePortTF; // 端口号输入框
private JButton sendBT; // 发送按钮
private JButton clearBT; // 清除聊天记录按钮
private DatagramSocket datagramSocket; // 用于后面功能的实现
}
接下来在 GuiChat 中定义一个 setUpUI()方法,实现用户图形界面的设置,如例 10-5 所
示。
例 10-5 setUpUl( )方法
private void setUpUI() { // 初始化 Swing 窗口
// 初始化窗口
setTitle("GUI 聊天");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400); // 设置窗口的大小
setResizable(false); // 窗口大小不可调整
setLocationRelativeTo(null); // 窗口居中
// 窗口的 NORTH 部分
stateLB = new JLabel("当前还未启动监听");
stateLB.setHorizontalAlignment(JLabel.RIGHT);
// 窗口的 CENTER 部分
centerTextArea = new JTextArea(); // 聊天记录显示区域
centerTextArea.setEditable(false);
centerTextArea.setBackground(new Color(211, 211, 211));
// 窗口的 SOUTH 部分
southPanel = new JPanel(new BorderLayout());
inputTextArea = new JTextArea(5, 20);// 内容输入区域
bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
ipTextField = new JTextField("127.0.0.1", 8);
remotePortTF = new JTextField(String.valueOf(DEFAULT_PORT), 3);
sendBT = new JButton("发送");
clearBT = new JButton("清屏");
bottomPanel.add(ipTextField);
bottomPanel.add(remotePortTF);
bottomPanel.add(sendBT);
bottomPanel.add(clearBT);
southPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);
southPanel.add(bottomPanel, BorderLayout.SOUTH);
// 添加窗口 NORTH、CENTER、SOUTH 部分的组件
add(stateLB, BorderLayout.NORTH);
add(new JScrollPane(centerTextArea), BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
setVisible(true);
}
在 setUpUI 方法中,使用 Borderlayou 布局管理器将图形用户界面分为 NORTH(代码的 9~10
行)、CENTER(代码的 12~ 14 行)、SOUTH(代码的 16~28 行)三部分。NORTH 部分放置一个
JLabel 标签 stateLB,用来显示监听状态信息;CENTER 部分放置了一个 JTextArea 文本域,用来
显示聊天内容;SOUTH 部分放置了一个 JPanel 面板 southPanel,在 southPanel 中又放置了一
一个JTextArea 文本域inputTextArea和一个JPanel 面板bottomPanel,inputTextArea 用来显示
用户输入的聊天内容,bottomPanel 中放置四个组件,ipTextField 和 remotePortTF 文本框分
别用来填写目标计算机的 IP 地址和端口号,sendBT 和 clearBT 按钮分别用于发送聊天位息和
清除 centerTextArea 文本域中显示的聊天内容。

2.编写事件处理器(发送信息)
界面开发完成后,还需要为界面上的按钮添加监听事件,在 GuiChat 类中,sendBT 和 clearBT
按钮的监听事件封装在 setListener( )方法中,如例 13-6 所示。
例 13-6 setListener( )方法
private void setListener() {
// 为 sendBT 按钮添加事件监听器
sendBT.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 获取发送的目标 ip 地址和端口号
final String ipAddress = ipTextField.getText();
final String remotePort = remotePortTF.getText();
// 判断 ip 地址和端口号是否为空
if (ipAddress == null || ipAddress.trim().equals("")
|| remotePort == null || remotePort.trim().equals("")) {
JOptionPane.showMessageDialog(GuiChat.this, "请输入 IP 地址和端口
号");
return;
}
if (datagramSocket == null || datagramSocket.isClosed()) {
JOptionPane.showMessageDialog(GuiChat.this, "监听不成功");
return;
}
// 获得需要发送的内容
String sendContent = inputTextArea.getText();
byte[] buf = sendContent.getBytes();
try {
// 将发送的内容显示在自己的聊天记录中
centerTextArea.append("我对 " + ipAddress + ":" + remotePort
+ " 说:\n" + inputTextArea.getText() + "\n\n");
// 添加内容后,使滚动条自动滚动到最底端
centerTextArea.setCaretPosition(centerTextArea.getText()
.length());
// 发送数据
datagramSocket.send(new DatagramPacket(buf, buf.length,
InetAddress.getByName(ipAddress), Integer
.parseInt(remotePort)));
inputTextArea.setText("");
} catch (IOException e1) {
JOptionPane.showMessageDialog(GuiChat.this, "出错了,发送不成功");
e1.printStackTrace();
}
};
});
// 为 clearBT 按钮添加事件监听器
clearBT.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
centerTextArea.setText(""); // 清空聊天记录的内容
}
});
}
代码的第 3~38 行为 sendBT 按钮添加点击事件监听器,在监听器中首先分别从文本框
ipTetField 和 remotePort 中获取目标 IP 和端口号,判断它们是否为 null 或者空字符串"",若
不为空,则从 inputTextArea 中获取要发送的数据,把数据、IP 地址和端口号封装到
DatagramPacket 数据包,通过 datagramSocket 的 send () 方法发送,同时在 centerTextArea 中
显示出发送的内容。
代码的第 40~44 行为 clearBT 按钮添加点击事件监听器,当点击 clearBT 按钮时,会将
centerTextArea 文本域中显示的聊天内容清空。
3.DatagramSocket 的启动监听(接收信息)
UDP 通信时,需要使用 DatagramSocket 启动监听,DatagramSocket 监听的代码定
义在 GuiChat 类的 initSocket()方法中,initSocket()方法需要实现两个功脂第一个是接收用户填
写程序监听的端口号,如例 13-7 所示。
例 13-7 DatagramSocket 的启动监听
private void initSocket() {
int port = DEFAULT_PORT;
while (true) {
try {
if (datagramSocket != null && !datagramSocket.isClosed()) {
datagramSocket.close();
}
try { // 判断端口号是否在 1-65535 之间
port = Integer.parseInt(JOptionPane.showInputDialog(this,
"请输入端口号", "端口号",
JOptionPane.QUESTION_MESSAGE));
if (port < 1 || port > 65535) {
throw new RuntimeException("端口号超出范围");
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"你输入的端口不正确,请输入 1-65535 之间的数");
continue; // 端口不正确重新填写
}
// 启动 DatagramSocket
datagramSocket = new DatagramSocket(port);
startListen(); // 调用 startListen 方法
// 在 stateLB 中显示程序监听的端口号
stateLB.setText("已在 " + port + " 端口监听");
break;
} catch (SocketException e) { // 端口号被占用重新填写
JOptionPane.showMessageDialog(this, "端口已被占用,请重新设置端
口");
stateLB.setText("当前还未启动监听");
}
}
}
例 13—7 中,initSocket( )方法使用了 while(true)循环,目的是为了当用户填写的
监听口号不满足要求时,反复地弹出输入窗口,让用户重新输人端口号,其过程如下:
首先弹一个输入窗口,接收用户输入的监听端口号,如果端口号不在 1~65535 之中,会
抛出一个异常,在 catch()块中捕获异常后,会弹出窗口提示"你输人的端口不正确,
请输人 1-655353 之间的数"的信息,同时执行 continue 语句,重新开始循环,如果用
户输人的监听端口号在 1~65535 之间,就使用该端口号创建 DatagramSocket 对象。本
节在"脚下留心"中讲过,如果 DatagramSocke 监听的端口号被其他程序占用,也会抛
出一个异常,在 25~28 行的 catch()代码块中会捕获这个异常,并弹出窗口提示"端口
已被占用,请重新设置端口"的信息,然后继续执行循环。只有填写的监听端口号正确
并且未被占用时,才会成功创建 DatagramSocket 对象,之后调用 startListen()方法,
执行 break 语句退出 while 循环。
initSocket()方法还有一个功能是接收消息,这个功能封装在 startListen()方法中,
为了避免在接收消息时 AWT 线程发生阻塞,需要在 startListen()方法中开启一个新的
线程,把接收消息的实现放在新线程的 run()方法中,如例 13-8 所示。
例 13-8 startListen()方法
private void startListen() {
new Thread() {
private DatagramPacket p;
public void run() {
byte[] buf = new byte[1024];
// 创建 DatagramPacket
p = new DatagramPacket(buf, buf.length);
while (!datagramSocket.isClosed()) {
try {
datagramSocket.receive(p); // 接收聊天消息
// 添加到聊天记录框
centerTextArea.append(p.getAddress().getHostAddress()
+ ":"
+ ((InetSocketAddress) p.getSocketAddress())
.getPort() + " 对我说:\n"
+ new String(p.getData(), 0, p.getLength())
+ "\n\n");
// 使滚动条自动滚动到最底端
centerTextArea.setCaretPosition(centerTextArea
.getText().length());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
例 13-8 中,startListen()方法很简单,在该方法中开启了一个新的线程,在 run()方法中使
用 while 循环,判断 DatagramSocket 对象有没有关闭,如果没有关闭就不停地接收消息,并
显示在 centerTextArea 文本域中。
4.功能测试
至此,所有的功能模块都已实现,在 GuiChat 的构造方法中分别调用 setUpUl()、
initSocket()与 setListener()方法,就完成了 UDP 聊天程序,如例 13-9 所示。
例 13-9 GuiChat 的构造方法
public GuiChat() {
setUpUI();
initSocket();
setListener();
}
public static void main(String[] args) {
new GuiChat();
}
例 13-9 中,在 GuiChat 类的 main()方法中创建 GuiChat 实例对象,就可以运行这个 UDP 聊
天程序。这里启动两个聊天程序的窗口,分别填写对方的 IP 地址和监听的端口号,测试程
序的聊天功能,如图 13-13 所示。



from shengerguan


由于两个聊天程序都在本机启动,从图 13-13 中可以看到,IP 地址填写的都是本地回环 地址 127.0.0.1,而且只要保证发送端填写的目标端口号和接收端口号一致,就能实现两个 窗口正常的聊天功能。


上一篇:
UDP 网络程序案例
下一篇:
Java TCP 通信
相关推荐
最近更新
学校免费发布信息关闭
如有图片请发邮件到:edu63@foxmail.com,审核后显示

 换一张

确认提交
完善补充本文信息关闭
非常感谢您帮助完善补充本文信息


 换一张

确认提交