1. UDP通信
1.1. 简述
-
UDP指的是用户数据报协议(User Datagram Protocol)
-
UDP是
无连接通信协议
,即在数据传输时,数据的发送端和接收端不建立逻辑连接
。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。 -
由于使用UDP协议
消耗系统资源小,通信效率高
,所以通常都会用于音频、视频和普通数据
的传输 -
例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议
1.2. Java中的UDP通信
- UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念
- Java提供了DatagramSocket类作为基于UDP协议的Socket
1.3. Java使用UDP协议
1.3.1. DatagramSocket类
-
构造方法
方法名 说明 DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任何可用端 -
DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机的指定端口-
-
DatagramSocket的 相关方法
方法名 说明 void send(DatagramPacket p) 发送数据报包 void close() 关闭数据报套接字 void receive(DatagramPacket p) 从此套接字接受数据报包
1.3.2. 发送数据的步骤
-
创建发送端的Socket对象(DatagramSocket)
-
创建数据,并把数据打包
-
调用DatagramSocket对象的方法发送数据
-
关闭发送端
- 代码演示
package link.xiaomo.test3;
import java.io.IOException;
import java.net.*;
public class Demo01 {
public static void main(String[] args) throws IOException {
// 1 创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
// 2 把要发送的数据打包
String text = "你好";
byte[] bytes = text.getBytes("gbk");
InetAddress addr = InetAddress.getByName("127.0.0.1");
// 参1 要发送的数据 byte[]类型 参2 是要发送的数据的长度
// 参3 InetAddress对象 表示ip 参4 接收的程序的端口号
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, addr, 10000);
// 3 发送数据
socket.send(packet);
// 4 关闭socket
socket.close();
}
}
1.3.3. 接收数据的步骤
-
创建接收端的Socket对象(DatagramSocket)
-
创建一个数据包,用于接收数据
-
调用DatagramSocket对象的方法接收数据
-
解析数据包,并把数据在控制台显示
-
关闭接收端
-
构造方法
方法名 说明 DatagramPacket(byte[] buf, int len) 创建一个DatagramPacket用于接收长度为len的数据包 -
相关方法
方法名 说明 byte[] getData() 返回数据缓冲区 int getLength() 返回要发送的数据的长度或接收的数据的长度 -
示例代码
package link.xiaomo.test3;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Demo02 {
public static void main(String[] args) throws IOException {
//1 创建DatagramSocket 用来发送数据
DatagramSocket ds = new DatagramSocket(11000);
//2 创建一个空的数据包对象 用来存放接收的数据
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3 接收数据
System.out.println(1111);
ds.receive(dp); //代码在这里等待接收数据
System.out.println(222);
//从数据包中获取网络传来的数据
byte[] data = dp.getData();
//打印数据 dp.getLength()是获取网络传来的数据的长度 注意和数组的长度是有区别的
System.out.println(new String(data, 0, dp.getLength(),"gbk"));
// 4 关闭socket
ds.close();
}
}
1.4.UDP三种通讯方式
- 单播:一对一发送消息。多用于两个主机之间的端对端通信。
- 组播:对一个分组中的所有IP发消息。组播用于对一组特定的主机进行通信。
- 广播:用于一个主机对整个局域网上所有主机上的数据通信。
额外说明:组播地址
在 IPv4 中,组播依赖于 D 类 IP 地址,范围从 224.0.0.0 到 239.255.255.255,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址3类:
- 局部链接多播地址范围在 224.0.0.0\~224.0.0.255,这是为路由协议和其它用途保留的地址,路由器并不转发属于此范围的IP包;
- 预留多播地址为 224.0.1.0\~238.255.255.255,可用于全球范围(如Internet)或网络协议;
- 管理权限多播地址为 239.0.0.0\~239.255.255.255,可供组织内部使用,类似于私有 IP 地址,不能用于 Internet,可限制多播范围。
1.5. 单播案例
1.5.1. 思路
UDP发送数据:数据来自于键盘录入,可以一直发,直到输入的数据是886,发送数据程序结束
因为接收端不知道发送端什么时候停止发送,所以此处应该采用死循环来接收
1.5.2.代码实现
/*
UDP发送数据:
数据来自于键盘录入,直到输入的数据是886,发送数据结束
*/
package link.xiaomo.test4;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
try (//1 创建socket对象
DatagramSocket ds = new DatagramSocket(20000);) {
//2 键盘录入要发送的数据
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入要发送的数据");
String text = sc.next();
//如果输入886就结束程序
if ("886".equals(text)) {
System.out.println("88");
break;
}
//3 把要发送的数据打包
byte[] bytes = text.getBytes();
//目标地址
InetAddress addr = InetAddress.getByName("192.168.50.228");
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, addr, 30000);
//4 发送数据
ds.send(dp);
//5 关闭socket
//ds.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
UDP接收数据:
因为接收端不知道发送端什么时候停止发送,故采用死循环接收
*/
package link.xiaomo.test4;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.util.Scanner;
public class Demo02 {
public static void main(String[] args) {
try (//1 创建socket对象
DatagramSocket ds = new DatagramSocket(30000);) {
//2创建数据包里接收数据
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
while (true) {
//3 循环一直接收数据
ds.receive(dp);
//4打印获取的数据
//dp.getSocketAddress() 获取到来自于发送方的ip和端口
SocketAddress remoteAddr = dp.getSocketAddress();
String text = new String(dp.getData(), 0, dp.getLength());
System.out.println("接收到了来自" + remoteAddr + "的数据" + text);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.6. 组播案例
1.6.1. 思路
- 发送端
- 创建发送端的Socket对象(DatagramSocket)
- 创建数据,并把数据打包(DatagramPacket)
- 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑。但是在组播当中,这里是发给组播地址)
- 释放资源
- 接收端
- 创建接收端Socket对象(MulticastSocket)
- 创建一个箱子(DatagramPacket),用于接收数据
- 把当前计算机绑定一个组播地址
- 将数据接收到箱子中
- 解析数据包,并打印数据
- 释放资源
1.6.2. 代码实现
// 发送端
public class SendDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
String s = "hello 组播";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("224.0.1.0");
int port = 10000;
// 2. 创建数据,并把数据打包(DatagramPacket)
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
// 3. 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑但是在组播当中,这里是发给组播地址)
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
// 接收端
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 1. 创建接收端Socket对象(MulticastSocket)
MulticastSocket ms = new MulticastSocket(10000);
// 2. 创建一个箱子,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
// 3. 把当前计算机绑定一个组播地址,表示添加到这一组中.
ms.joinGroup(InetAddress.getByName("224.0.1.0"));
// 4. 将数据接收到箱子中
ms.receive(dp);
// 5. 解析数据包,并打印数据
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
// 6. 释放资源
ms.close();
}
}
1.7. 广播案例
1.7.1. 思路
实现广播的方式:发送的目标地址 写成255.255.255.255就可以了!
- 发送端
- 创建发送端Socket对象(DatagramSocket)
- 创建存储数据的箱子,将广播地址封装进去
- 发送数据
- 释放资源
- 接收端
- 创建接收端的Socket对象(DatagramSocket)
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台显示
- 关闭接收端
1.7.2. 代码实现
// 发送端
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
// 2. 创建存储数据的箱子,将广播地址封装进去
String s = "广播 hello";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("255.255.255.255");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
// 3. 发送数据
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
// 接收端
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1. 创建接收端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(10000);
// 2. 创建一个数据包,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
// 3. 调用DatagramSocket对象的方法接收数据
ds.receive(dp);
// 4. 解析数据包,并把数据在控制台显示
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
// 5. 关闭接收端
ds.close();
}
}
1.8. 同时可以收发数据的UDP案例
- 发数据的任务
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UdpSendRunnable implements Runnable {
private String addr;//目标ip
private int port;//目标端口
DatagramSocket socket;
Scanner sc = new Scanner(System.in);
public UdpSendRunnable(DatagramSocket socket) {
this.socket = socket;
}
public UdpSendRunnable(DatagramSocket socket, String addr, int port) {
this.socket = socket;
this.addr = addr;
this.port = port;
}
@Override
public void run() {
// 2 把要发送的数据打包
while (true) {
try {
String text = sc.next();
byte[] bytes = text.getBytes();
InetAddress iAddr = InetAddress.getByName(addr);
// 参1 要发送的数据 byte[]类型 参2 是要发送的数据的长度
// 参3 InetAddress对象 表示ip 参4 接收的程序的端口号
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, iAddr, port);
// 3 发送数据
socket.send(packet);
} catch (IOException e) {
System.out.println(e);
}
}
}
}
- 收数据的代码
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
public class UdpReceiveRunnable implements Runnable {
DatagramSocket socket;
public UdpReceiveRunnable(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
while (true) {
try {
// 3 接收的代码
// 1创建一个空的数据包对象
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
// 2接收数据 存到数据包DatagramPacket对象里
socket.receive(packet); // 这里会接收数据 是阻塞的
// 3 打印数据
byte[] data = packet.getData();
// 获取发送信息的 地址
SocketAddress socketAddress = packet.getSocketAddress();
System.out.println("来自于" + socketAddress + "发的数据:" + new String(data, 0, packet.getLength()));
} catch (IOException e) {
System.out.println(e);
}
}
}
}
- 程序入口
import java.io.IOException;
import java.net.DatagramSocket;
public class Demo04 {
public static void main(String[] args) throws IOException {
// 1 创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket(12000);
// 创建发送任务的对象
UdpSendRunnable udpSendRunnable = new UdpSendRunnable(socket, "127.0.0.1", 14000);
// 创建接收任务的对象
UdpReceiveRunnable udpReceiveRunnable = new UdpReceiveRunnable(socket);
new Thread(udpSendRunnable).start(); //开启线程
new Thread(udpReceiveRunnable).start();//开启线程
// 4 关闭socket
// socket.close();
}
}
Comments NOTHING