【mie haha的博客】转载请注明出处(万分感谢!):
https://blog.csdn.net/qq_40315080/article/details/88830295
实现通信包含发送方和接收方两部分,但这两部分除了端口设置,其他代码完全相同,非常简单。
因为不同于之前写的聊天室篇使用TCP/IP进行通信,UDP通信不需要用Scocket建立起通信管道,直接发送包即可(不管对方能否收到),所以代码是对称的,只是起的名字不同而已。
以发送方为例:
1.它需要不停地发送消息和接收消息(发送方类似服务器,不只发送信息,也需要接收消息),需要用一个while(true)循环实现。
2.同时,发送方不只给一个接收方发送消息,可以给多个接收端发消息就像有好多个发送方在同时工作一样,这就需要把接收发送消息的功能写在线程中。
3.为了能更方便,还要给发送方写一个界面。
4.这样就可以在界面上点击发送按钮进行消息发送了,还需要一个监听器。
即:
发送方=监听器类(响应操作)+界面类(方便操作)+线程类(实现功能)
注:我把发送消息写在监听器类中,接收消息写在了线程类中
代码:
发送方:
【监听器类】
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import javax.swing.JTextField;
import javax.swing.plaf.synth.SynthSpinnerUI;
//监听器类,继承ActionListener
public class ButtonListener implements ActionListener {
//输入框
private JTextField jt;
//创建要用来发送的本地地址
SocketAddress localAddr = new InetSocketAddress("192.168.28.43", 6666);
private DatagramSocket dSender;
//目标发送地址
SocketAddress destAdd = new InetSocketAddress("192.168.28.43",9998);
public ButtonListener(JTextField jt) {
this.jt = jt;
}
//重写捕捉点击Action e鼠标点击时的响应操作
public void actionPerformed(ActionEvent e) {
try {
if(dSender==null)
// 2.创建发送的Socket对象
dSender = new DatagramSocket(localAddr);
int count = 0;
while (count<1) {
count++;
byte buffer[] = (jt.getText()).getBytes();// 3.要发送的数据
// 4.发送数据的目标地址和端口
// 5.创建要发送的数据包,指定内容,指定目标地址
DatagramPacket dp = new DatagramPacket(buffer, buffer.length, destAdd);
dSender.send(dp);//发送
System.out.println("数据已发送: " + jt.getText());
jt.setText("");
Thread.sleep(100);
}
} catch (Exception el) {
}
}
}
【界面类】
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ReceiveFrame {
public static void main(String[] args){
ReceiveFrame rf = new ReceiveFrame();
rf.initUI();
}
public void initUI(){
JFrame frame = new JFrame("接收方");
frame.setSize(400,600);
frame.setDefaultCloseOperation(3);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
FlowLayout flow = new FlowLayout();
frame.setLayout(flow);
JTextArea ja = new JTextArea();
Dimension dms = new Dimension(300,200);
ja.setPreferredSize(dms);
frame.add(ja);
JTextField jt = new JTextField();
Dimension dm = new Dimension(300,30);
jt.setPreferredSize(dm);
frame.add(jt);
JButton jb = new JButton("发送");
frame.add(jb);
frame.setVisible(true);
ButtonListener but = new ButtonListener(jt);
jb.addActionListener(but);
ReceiveThread rt = new ReceiveThread(ja);
rt.start();
}
}
【线程类】
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.swing.JTextArea;
//线程
public class ReceiveThread extends Thread{
//信息显示部分
private JTextArea ja;
//接收地址
SocketAddress localAddr = new InetSocketAddress("192.168.28.43", 9999);
//数据包
DatagramSocket recvSocket;
//把界面上的信息显示部分传过来,构造函数
public ReceiveThread(JTextArea ja){
this.ja=ja;
}
//重写run方法
public void run() {
try {
if(recvSocket==null)
recvSocket = new DatagramSocket(localAddr);
while(true){
byte[] buffer = new byte[20]; // 3.指定接收缓冲区大小:每个包20字节
// 4.创建接收数据包对象
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 5.阻塞等待数据到来,如果收到数据,存入packet中的缓冲区中
System.out.println("UDP服务器等待接收数据:");
recvSocket.receive(packet); // 6.在此等待接收对方发的UDP包
System.out.println("get");
// 得到发送方的IP和端口
SocketAddress address = packet.getSocketAddress();
// 转换接收到的数据为字符串
String msg = new String(packet.getData()).trim();
msg+="\r\n";
ja.append(msg);
// 接收到后,打印出收到的数据长度
System.out.println("recv is:" + msg + " from:" + address);
}
} catch (Exception el) {
}
}
}
接收方除了监听器类中的端口设置,其他与发送方完全相同。
接收端:
【监听器类】
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import javax.swing.JTextField;
import javax.swing.plaf.synth.SynthSpinnerUI;
public class ButtonListener implements ActionListener {
//输入框
private JTextField jt;
//创建要用来发送的本地地址
SocketAddress localAddr = new InetSocketAddress("192.168.28.43", 7777);
private DatagramSocket dSender;
//目标发送地址
SocketAddress destAdd = new InetSocketAddress("192.168.28.43",9999);
public ButtonListener(JTextField jt) {
this.jt = jt;
}
public void actionPerformed(ActionEvent e) {
try {
if(dSender==null)
// 2.创建发送的Socket对象
dSender = new DatagramSocket(localAddr);
int count = 0;
while (count<1) {
count++;
byte buffer[] = (jt.getText()).getBytes();// 3.要发送的数据
// 4.发送数据的目标地址和端口
// 5.创建要发送的数据包,指定内容,指定目标地址
DatagramPacket dp = new DatagramPacket(buffer, buffer.length, destAdd);
dSender.send(dp);// 6.发送
System.out.println("数据已发送: " + jt.getText());
jt.setText("");
Thread.sleep(100);
}
} catch (Exception el) {
}
}
}
【界面类】
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ReceiveFrame {
public static void main(String[] args){
ReceiveFrame rf = new ReceiveFrame();
rf.initUI();
}
public void initUI(){
JFrame frame = new JFrame("接收方");
frame.setSize(400,600);
frame.setDefaultCloseOperation(3);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
FlowLayout flow = new FlowLayout();
frame.setLayout(flow);
JTextArea ja = new JTextArea();
Dimension dms = new Dimension(300,200);
ja.setPreferredSize(dms);
frame.add(ja);
JTextField jt = new JTextField();
Dimension dm = new Dimension(300,30);
jt.setPreferredSize(dm);
frame.add(jt);
JButton jb = new JButton("发送");
frame.add(jb);
frame.setVisible(true);
ButtonListener but = new ButtonListener(jt);
jb.addActionListener(but);
ReceiveThread rt = new ReceiveThread(ja);
rt.start();
}
}
【线程类】
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.swing.JTextArea;
//线程
public class ReceiveThread extends Thread{
//信息显示部分
private JTextArea ja;
//接收地址
SocketAddress localAddr = new InetSocketAddress("192.168.28.43", 9998);
//数据包
DatagramSocket recvSocket;
//把界面上的信息显示部分传过来,构造函数
public ReceiveThread(JTextArea ja){
this.ja=ja;
}
//重写run方法
public void run() {
try {
if(recvSocket==null)
recvSocket = new DatagramSocket(localAddr);
while(true){
byte[] buffer = new byte[20]; // 3.指定接收缓冲区大小:每个包20字节
// 4.创建接收数据包对象
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 5.阻塞等待数据到来,如果收到数据,存入packet中的缓冲区中
System.out.println("UDP服务器等待接收数据:");
recvSocket.receive(packet); // 6.在此等待接收对方发的UDP包
System.out.println("get");
// 得到发送方的IP和端口
SocketAddress address = packet.getSocketAddress();
// 转换接收到的数据为字符串
String msg = new String(packet.getData()).trim();
msg+="\r\n";
ja.append(msg);
// 接收到后,打印出收到的数据长度
System.out.println("recv is:" + msg + " from:" + address);
}
} catch (Exception el) {
}
}
}
这里注意一下,发送方和接收方的端口的设置:
还是以发送方为例,要发送消息需要有一个自己发出消息的地址(本机地址+指定一个端口),目标发送端口(要发送到的地方的地址+端口),接收方将在这个地方进行监听接收。
(在监听器类中)。要接收消息时也有一个接收地址(在线程类中)。
两台电脑A和B分别作为发送端和接收端,怎样连接呢?
windows+r打开命令行,输入ipconfig查看自己的地址,查看无线局域网WLAN的IPv4地址,这样A和B都得到了自己的地址a,b。
A和B分别随便指定一个端口发出消息from_a,from_b,再约定要发送到的目标端口aim_a,aim_b。
匹配如图:
以上代码中的监听器类和线程类中的地址(和端口)可按以上方法修改,这样就可以在两台电脑上进行互通了~
运行效果:
(如果把发送方和接收方的地址都改成自己的地址,就可以在自己的电脑上自己和自己连通了*~*)
界面还可以再继续美化。
以上代码全部完整。
如有错误,欢迎指出。