本学期计算机网络课程要求完成一个TCP和一个UDP的通信程序,我完成了功能的简单实现。我在上一篇博客已经讲了TCP的具体实现,接下来讲一讲UDP程序的实现。
效果展示
初始界面
连接成功
互发消息
以下是具体实现过程:
一、项目结构
项目结构
下面说说每个类的具体作用:
UDPClient:UDP客户端的核心代码部分;
UDPServer:UDP服务器端的核心代码部分;
UDPFrame:UDP客户端和服务器端的界面搭建;
UDPSend:UDP客户端或服务器端的一端向另一端发送消息;
UDPReceive:UDP客户端或服务器端的一端接收另一端发送的消息;
ReminderTextAdapter:网上查阅的代码,非核心内容实现部分,主要用于灰色提示字hint功能的实现(swing中的实现方法不太熟悉);
注:在UDP通信中,没有TCP那样明确的ServerSocket,只是通过Datagram数据报的方式传递给另外的DatagramSocket,因此本项目中虽然有客户端与服务器端的区别,但本质上二者并无区别(即客户端与服务器端实现方式基本相同),因此客户端和服务器端需要的界面布局也相同,故我也把二者的界面抽取成了一个类,在此特地说明一下。
二、完整代码
1.UDPClient
代码如下:
package udpCommunication;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPClient implements ActionListener, Runnable {
private DatagramSocket datagramSocket;
private Thread thread = null;
private UDPFrame clientFrame;
public UDPClient() {
JPanel panel = new JPanel();
clientFrame = new UDPFrame("UDPClient");
clientFrame.add(panel);
clientFrame.placeComponents(panel);
clientFrame.setVisible(true);
clientFrame.getConnect().addActionListener(e -> {
try {
//host:"127.0.0.1"
//String host = UDPClientFrame.getIpField().getText();
//port:8887
int dSocketPort = Integer.parseInt(clientFrame.getDSocketField().getText());
datagramSocket = new DatagramSocket(dSocketPort);
clientFrame.getTextArea().append("[连接] : 正在连接中……\n");
clientFrame.getTextArea().append("\t连接成功\n");
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
} catch (IOException ex) {
ex.printStackTrace();
}
});
clientFrame.getTransmit().addActionListener(this);
clientFrame.getTextArea().append("……UDP客户端启动……\n");
System.out.println("……UDP客户端启动……\n");
}
public static void main(String[] args) {
new UDPClient();
}
//向服务器端发送数据
@Override
public void actionPerformed(ActionEvent e) {
//String send = UDPClientFrame.getSendField().getText();
//port:8888
//发送消息
new UDPSend(datagramSocket, clientFrame, "客户端").sendMessages();
}
//使用线程处理客户端接收服务器端的数据
@Override
public void run() {
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
new UDPReceive(datagramSocket, datagramPacket, clientFrame, "服务器端").receiveMessages();
}
}
2.UDPServer
代码如下:
package udpCommunication;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.*;
public class UDPServer implements ActionListener, Runnable {
private DatagramSocket datagramSocket;
private Thread thread = null;
private UDPFrame serverFrame;
public UDPServer() {
serverFrame = new UDPFrame("UDPServer");
JPanel panel = new JPanel();
serverFrame.add(panel);
serverFrame.placeComponents(panel);
serverFrame.setVisible(true);
//port:8888
serverFrame.getConnect().addActionListener(e -> {
try {
int dSocketPort = Integer.parseInt(serverFrame.getDSocketField().getText());
datagramSocket = new DatagramSocket(dSocketPort);
serverFrame.getTextArea().append("[连接] : 正在连接中……\n");
serverFrame.getTextArea().append("\t连接成功\n");
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
} catch (IOException ex) {
ex.printStackTrace();
}
});
serverFrame.getTransmit().addActionListener(this);
serverFrame.getTextArea().append("……UDP服务器启动……\n");
System.out.println("……UDP服务器启动……\n");
}
public static void main(String[] args) {
new UDPServer();
}
//向客户端发送数据
@Override
public void actionPerformed(ActionEvent e) {
new UDPSend(datagramSocket, serverFrame, "服务器端").sendMessages();
}
//使用线程处理服务器端接收客户端的数据
@Override
public void run() {
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
new UDPReceive(datagramSocket, datagramPacket, serverFrame, "客户端").receiveMessages();
}
}
3.UDPFrame
代码如下:
package udpCommunication;
import javax.swing.*;
import java.awt.*;
/**
* ip
* DatagramSocket(hint:port)
* DatagramPacket(hint:port)
* send
* showArea
*/
public class UDPFrame extends JFrame {
private static JTextField ipField;
private static JTextField dSocketField;
private static JTextField dPacketField;
private static JTextField sendField;
private static JTextArea textArea;
private static JButton connect;
private static JButton transmit;
public UDPFrame(String title) {
setSize(700, 480);
setLocationRelativeTo(null);//居中显示
setResizable(false);//设置不允许改变窗体大小
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(title);
}
public JTextField getIpField() {
return ipField;
}
public JTextField getDSocketField() {
return dSocketField;
}
public JTextField getDPacketField() {
return dPacketField;
}
public JTextField getSendField() {
return sendField;
}
public JTextArea getTextArea() {
return textArea;
}
public JButton getConnect() {
return connect;
}
public JButton getTransmit() {
return transmit;
}
public static void placeComponents(JPanel panel) {
panel.setLayout(null);
Font font1 = new Font("宋体", Font.BOLD, 16);
Font font2 = new Font("宋体", Font.PLAIN, 16);
//创建JLabel
JLabel ip = new JLabel("IP");
ip.setFont(font1);//设置JLabel的字体
ip.setBounds(80, 30, 135, 30);
panel.add(ip);
JLabel dSocketLabel = new JLabel("DatagramSocket");
dSocketLabel.setFont(font1);
dSocketLabel.setBounds(80, 65, 135, 30);
panel.add(dSocketLabel);
JLabel dPacketLabel = new JLabel("DatagramPacket");
dPacketLabel.setFont(font1);
dPacketLabel.setBounds(80, 100, 135, 30);
panel.add(dPacketLabel);
JLabel send = new JLabel("Send");
send.setFont(font1);
send.setBounds(80, 135, 135, 30);
panel.add(send);
JLabel text = new JLabel("Text");
text.setFont(font1);
text.setBounds(80, 170, 135, 30);
panel.add(text);
//创建文本域
ipField = new JTextField(30);
ipField.setFont(font2);
ipField.addFocusListener(new ReminderTextAdapter(ipField, "默认:127.0.0.1"));
ipField.setBounds(220, 30, 360, 30);
panel.add(ipField);
dSocketField = new JTextField(30);
dSocketField.setFont(font2);
dSocketField.addFocusListener(new ReminderTextAdapter(dSocketField, "port:"));
dSocketField.setBounds(220, 65, 360, 30);
panel.add(dSocketField);
dPacketField = new JTextField(30);
dPacketField.setFont(font2);
dPacketField.addFocusListener(new ReminderTextAdapter(dPacketField, "port:"));
dPacketField.setBounds(220, 100, 360, 30);
panel.add(dPacketField);
sendField = new JTextField(30);
sendField.setFont(font2);
sendField.addFocusListener(new ReminderTextAdapter(sendField, "请输入要发送的信息:"));
sendField.setBounds(220, 135, 360, 30);
panel.add(sendField);
textArea = new JTextArea();
textArea.setFont(font2);
textArea.setBounds(220, 170, 360, 160);
textArea.setEditable(false);
textArea.setLineWrap(false);
panel.add(textArea);
//给textArea添加滚动条
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(220, 170, 360, 160);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
panel.add(scrollPane);
scrollPane.setViewportView(textArea);
//添加两个按钮
connect = new JButton("连接");
connect.setFont(font1);
connect.setBounds(140, 360, 115, 40);
panel.add(connect);
transmit = new JButton("发送");
transmit.setFont(font1);
transmit.setBounds(440, 360, 115, 40);
panel.add(transmit);
}
}
4.UDPSend
代码如下:
package udpCommunication;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSend {
private DatagramSocket datagramSocket;
private UDPFrame udpFrame;
private String sender;
public UDPSend(DatagramSocket datagramSocket, UDPFrame udpFrame, String sender) {
this.datagramSocket = datagramSocket;
this.udpFrame = udpFrame;
this.sender = sender;
}
public void sendMessages() {
//String send = UDPServerFrame.getSendField().getText();
//发送消息
try {
System.out.println("请输入" + sender + "要发送的消息:");
String data = udpFrame.getSendField().getText();
String ip = udpFrame.getIpField().getText();
int dPacketPort = Integer.parseInt(udpFrame.getDPacketField().getText());
byte[] bytes = data.getBytes();
InetAddress inetAddress = InetAddress.getByName(ip);
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, inetAddress, dPacketPort);
datagramSocket.send(datagramPacket);
//udpFrame.getTextArea().append("\t" + sender + "发送消息成功\n");
udpFrame.getTextArea().append("\t\t\t[" + sender + "] : " + data + "\n");
String ipAddress = datagramPacket.getAddress().getHostAddress();
System.out.println("ipAddress: " + ipAddress);
System.out.println("ipField().getText(): " + ip);
} catch (Exception ex) {
ex.printStackTrace();
udpFrame.getTextArea().append("\t" + sender + "尚未连接成功,消息发送失败\n");
} finally {
//System.out.println("\t"+sender+"发送消息成功\n");
}
}
}
5.UDPReceive
代码如下:
package udpCommunication;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceive {
private DatagramSocket datagramSocket;
private DatagramPacket datagramPacket;
private UDPFrame udpFrame;
private String sender;
public UDPReceive(DatagramSocket datagramSocket, DatagramPacket datagramPacket, UDPFrame udpFrame, String sender) {
this.datagramSocket = datagramSocket;
this.datagramPacket = datagramPacket;
this.udpFrame = udpFrame;
this.sender = sender;
}
public void receiveMessages() {
try {
while (true) {
byte[] bytes = new byte[1024];
datagramPacket = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
String string = new String(data, 0, datagramPacket.getLength());
udpFrame.getTextArea().append("[" + sender + "] : " + string + "\n");
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
udpFrame.getTextArea().append(sender + "异常\n");
udpFrame.getTextArea().append(sender + "与客户端连接中断\n");
} finally {
udpFrame.getTextArea().append(sender + "关闭\n");
}
}
}
6.ReminderTextAdapter(参考别人的,非原创)
代码如下:
package udpCommunication;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
public class ReminderTextAdapter extends FocusAdapter {
//JTextField提示文字通用方法
JTextField textField;
String reminderString;
//初始化
public ReminderTextAdapter(JTextField textField, String reminderString) {
this.textField = textField;
this.reminderString = reminderString;
}
//焦点获得
@Override
public void focusGained(FocusEvent e) {
String tempString = textField.getText();
//String tempString = this.getText();
if (tempString.equals(reminderString)) {
textField.setText("");
textField.setForeground(Color.BLACK);
}
}
//焦点失去
@Override
public void focusLost(FocusEvent e) {
String tempString = textField.getText();
if (tempString.equals("")) {
textField.setForeground(Color.GRAY);
textField.setText(reminderString);
}
}
}
总结
该项目简单的实现了UDP客户端和服务器端的通信,但是功能比较简单,页面也比较简洁,没有实现多个客户端互聊的功能等,还有待完善。
刚开始写博客,大家觉得不错的点个赞吧!如果大家有什么建议或者问题的,欢迎评论,我会尽量与大家探讨问题的。