1.コネクション型ではなく、データグラムプロトコル。アプリケーションから送信されたデータをネットワーク層に送信すると、データのバックアップは保持されません。
対象:DNS、TFTP、SNMP、リアルタイム送信
2.UDPパケットの最大長は65507バイトです
3.UDPのコアAPIの概要
(1)DatagramSocket:UDPパケットを送受信するためのクラス
DatagramSocket():ポートとIPを指定せずに単純なインスタンスを作成します。この例を使用してUDPパケットを送信すると、ローカルで使用可能なポートが再利用されます。ipはマシンのIPです。
DatagramSocket(int port):固定ポートでリッスンするインスタンスを作成します。このポートを介して応答メッセージを受信できることを示します
DatagramSocket(int port、InetAddress localAddr):固定ポートとIPのインスタンスを作成します
receive(DatagramPacket d):UDPデータパケットを受信します。DatagramPacketは、その名前が示すように、UDPデータパケットのカプセル化クラスです。
send(DatagramPacket d):UDPデータパケットを送信します
setSoTimeout(int timeuot):残業を設定します。単位:ミリ秒
close():リソースを閉じて解放します
(2)DatagramPacket:UDP送受信のエンティティクラスであるUDPデータパケットのカプセル化クラスであると同時に、バイト配列、ターゲットアドレス、ターゲットポートなどのデータをUDPデータにカプセル化するメソッドがいくつかあります。パケット、またはUDPデータパケットはバイト配列に分解されます
DatagramPacket(byte [] buf、int offset、int length、InetAddress address、in port):offsetは、バイト配列の使用されている部分の開始インデックスです。最初の3つのパラメーターは、bufの使用範囲を指定します。後者の2つのパラメーターは、ターゲットマシンのIPとポートを指定します。これらは送信時にのみ有効ですが、受信時には無効です。
DatagramPacket(byte [] buf、int offset、SocketAddress address):SocketAddressは、InetAddress +ポートのパッケージと同等です。
setData(byte [] buf、int offset、int length):データをUDPデータにパックする方法
getData()、getOffset()、getLength()、getSocketAddress()
setAddress(InetAddress address)、setPort(int port)、setSocketAddress()
4. UDPユニキャスト、ブロードキャスト、マルチキャスト(または呼ばれる:マルチキャスト)
5.ブロードキャストアドレスの計算と理解(コンピュータネットワークコースでのサブネットの分割とスーパーネットの構築の内容を理解する必要があります。学習していない場合はスキップしてください)
既知の
ip:192.168.124.7
サブネットマスク:255.255.255.192
(または次のように記述されます:192.168.124.7 / 26)
ネットワークアドレスとブロードキャストアドレスを見つけます。
解決策:ネットワークアドレス= IP&サブネットマスク
最初の3バイトは255であるため、ipの最初の3バイトはそれ自体と同じです。したがって、4番目のバイトは次のようにカウントされます。
7 = 00000111
192 = 11000000
7&192 = 0
したがって、ネットワークアドレス:192.168.124.0
これはクラスCネットワークアドレスですが、そのサブネットマスクはデフォルトの255.255.255.0ではなく、255.255.255.192であり、バイナリに変換されます。これは、ホスト番号がサブネット番号として2ビットを借用したことを意味します。したがって、最初の24桁はネットワーク番号、中央の2桁はサブネット番号、最後の6桁はホスト番号です。
したがって、このタイプCネットワークは最大で2 ^ 2 = 4のサブネットに分割できます。これらの4つのサブネットの場合、使用可能なIPアドレスは(最初の3バイトを省略):0〜63、64〜127、128〜191、192〜255です。
明らかに、192.168.124.7のIPは最初のサブネットセグメントにあり、そのブロードキャストアドレスは192.168.124.63(つまり、最初のサブネットの最大IPアドレス)です。
注:異なるサブネットセグメント内のホストは、相互にブロードキャストできません。異なるサブネット内のホストのブロードキャストアドレスが異なるため、理由は実際には非常に単純です。したがって、それらは自然に相互にブロードキャストされます。
6. IDEAでは、ケースプラクティス:LAN検索ケース
(1)UDPはメッセージを受信して送り返します(UDPユニキャストをシミュレート)
(2)UDP LANブロードキャスト送信(アナログUDPブロードキャスト)
7.コード
(1)UDPはメッセージを受信して送り返します(UDPユニキャストをシミュレート)
最初にUDPProviderを起動し、次にUDPSearcherを起動します
UDPProvider.java
package UDPDemo;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author passerbyYSQ
* @create 2020-06-16 23:23
*/
public class UDPProvider {
public static void main(String[] args) throws IOException {
System.out.println("UDPProvider started.");
// 注意此处的port是监听本机的端口2000
DatagramSocket ds = new DatagramSocket(20000);
// 构建接收的实体
final byte[] buf = new byte[512];
DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);
// 接收。阻塞。
ds.receive(receivePkt);
// 从UDP数据包中获取发送者的ip和端口
String senderIp = receivePkt.getAddress().getHostAddress();
int port = receivePkt.getPort();
int dataLen = receivePkt.getLength();
String dataStr = new String(receivePkt.getData(), 0, dataLen);
System.out.println("UDPProvider receive from ip: " + senderIp
+ "\tport: " + port + "\tdata: " + dataStr);
// 构建一份回送的数据包
String responseData = "Receive data with len: " + dataLen;
byte[] responseBytes = responseData.getBytes();
DatagramPacket responsePkt = new DatagramPacket(responseBytes,
responseBytes.length, receivePkt.getAddress(), receivePkt.getPort());
ds.send(responsePkt);
// 结束
System.out.println("UDPProvider finished.");
ds.close();
}
}
UDPSearcher.java
package UDPDemo;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @author passerbyYSQ
* @create 2020-06-16 23:23
*/
public class UDPSearcher {
public static void main(String[] args) throws IOException {
System.out.println("UDPSearcher started.");
// 让系统随机分配端口,用于发送数据包
DatagramSocket ds = new DatagramSocket();
// 构建一份回送的数据包
String requestData = "Hello World";
byte[] requestBytes = requestData.getBytes();
DatagramPacket requestPkt = new DatagramPacket(requestBytes, requestBytes.length);
requestPkt.setAddress(InetAddress.getLocalHost()); // 发送给本机
requestPkt.setPort(20000); // 发送给20000
// 发送
ds.send(requestPkt);
// 构建接收的实体
final byte[] buf = new byte[512];
DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);
// 接收
ds.receive(receivePkt);
// 从UDP数据包中获取发送者的ip和端口
String senderIp = receivePkt.getAddress().getHostAddress();
int port = receivePkt.getPort();
int dataLen = receivePkt.getLength();
String dataStr = new String(receivePkt.getData(), 0, dataLen);
System.out.println("UDPSearcher receive from ip: " + senderIp
+ "\tport: " + port + "\tdata: " + dataStr);
// 结束
System.out.println("UDPSearcher finished.");
ds.close();
}
}
(2)UDP LANブロードキャスト送信(アナログUDPブロードキャスト)
最初にUDPProviderを起動し(複数起動できます)、次にUDPSearcherを起動します
UDPProvider.java
package UDPDemo2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.UUID;
/**
* 接收广播的多方(B,有多个)
*
* @author passerbyYSQ
* @create 2020-06-16 23:23
*/
public class UDPProvider {
public static void main(String[] args) throws IOException {
// 唯一标识
String sn = UUID.randomUUID().toString();
Provider provider = new Provider(sn);
provider.start();
// 读取任意一个字符,表示退出
System.in.read();
provider.exit();
}
private static class Provider extends Thread {
private final String sn;
// 标记状态
private boolean done = false;
// 用于接收和发送UDP数据包
private DatagramSocket ds = null;
public Provider(String sn) {
super();
this.sn = sn;
}
@Override
public void run() {
super.run();
System.out.println("UDPProvider started.");
try {
// 注意此处的port是监听本机的端口2000
ds = new DatagramSocket(20000);
while (!done) {
// 构建接收的实体
final byte[] buf = new byte[512];
DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);
// 接收。阻塞。
ds.receive(receivePkt);
// 从UDP数据包中获取发送者的ip和端口
String senderIp = receivePkt.getAddress().getHostAddress();
int port = receivePkt.getPort();
int dataLen = receivePkt.getLength();
String dataStr = new String(receivePkt.getData(), 0, dataLen);
System.out.println("UDPProvider receive from ip: " + senderIp
+ "\tport: " + port + "\tdata: " + dataStr);
// 解析端口
int responsePort = MessageCreator.parsePort(dataStr);
if (responsePort != -1) {
// 构建一份回送的数据包
String responseData = MessageCreator.buildWithSn(sn);
byte[] responseBytes = responseData.getBytes();
DatagramPacket responsePkt = new DatagramPacket(responseBytes,
responseBytes.length, receivePkt.getAddress(),
responsePort); // 注意回送到约定的端口,而不是receivePkt中的端口
ds.send(responsePkt);
}
}
} catch (Exception ignored) {
//e.printStackTrace();
// ds.receive(receivePkt); 接收处于阻塞,状态,此时ds.close会抛出异常
// 此异常忽略,不打印
} finally {
close();
}
// 结束
System.out.println("UDPProvider finished.");
}
// 停止监听。给外部调用
void exit() {
done = true;
close();
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
}
}
UDPSearcher.java
package UDPDemo2;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* 发送广播的一方(A)
* @author passerbyYSQ
* @create 2020-06-16 23:23
*/
public class UDPSearcher {
private static final int LISTEN_PORT = 30000;
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("UDPSearcher started.");
Listener listener = listen();
sendBroadcast();
System.in.read();
List<Device> devices = listener.getDevicesAndClose();
for (Device device : devices) {
System.out.println("devices:" + device.toString());
}
System.out.println();
System.out.println("UDPSearcher finished.");
}
private static Listener listen() throws InterruptedException {
System.out.println("UDPSearcher listener started.");
CountDownLatch countDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTEN_PORT, countDownLatch);
listener.start();
countDownLatch.await();
return listener;
}
// 发送广播
private static void sendBroadcast() throws IOException {
System.out.println("UDPSearcher sendBroadcast started.");
// 让系统随机分配端口,用于发送数据包
DatagramSocket ds = new DatagramSocket();
// 构建一份回送的数据包
String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
byte[] requestBytes = requestData.getBytes();
DatagramPacket requestPkt = new DatagramPacket(requestBytes, requestBytes.length);
requestPkt.setAddress(InetAddress.getByName("255.255.255.255")); // 发送给广播地址
requestPkt.setPort(20000); // 发送给20000
// 发送
ds.send(requestPkt);
ds.close();
// 结束
System.out.println("UDPSearcher sendBroadcast finished.");
}
private static class Device {
final int port;
final String ip;
final String sn; // 标识
public Device(int port, String ip, String sn) {
this.port = port;
this.ip = ip;
this.sn = sn;
}
@Override
public String toString() {
return "Device{" +
"port=" + port +
", ip='" + ip + '\'' +
", sn='" + sn + '\'' +
'}';
}
}
private static class Listener extends Thread {
private final int listenPort;
private final CountDownLatch countDownLatch;
private final List<Device> devices = new ArrayList<>();
private boolean done = false;
private DatagramSocket ds = null;
public Listener(int listenPort, CountDownLatch countDownLatch) {
super();
this.listenPort = listenPort;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
super.run();
// 通知已启动
countDownLatch.countDown();
try {
ds = new DatagramSocket(listenPort);
while (!done) {
// 构建接收的实体
final byte[] buf = new byte[512];
DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);
// 接收
ds.receive(receivePkt);
// 从UDP数据包中获取发送者的ip和端口
String senderIp = receivePkt.getAddress().getHostAddress();
int port = receivePkt.getPort();
int dataLen = receivePkt.getLength();
String dataStr = new String(receivePkt.getData(), 0, dataLen);
System.out.println("UDPSearcher receive from ip: " + senderIp
+ "\tport: " + port + "\tdata: " + dataStr);
// 解析
String sn = MessageCreator.parseSn(dataStr);
if (sn != null) {
Device device = new Device(port, senderIp, sn);
devices.add(device);
}
}
} catch (Exception ignored) {
//e.printStackTrace();
} finally {
close();
}
// 结束
System.out.println("UDPSearcher listener finished.");
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
List<Device> getDevicesAndClose() {
done = true;
close();
return devices;
}
}
}
CountDownLatchについて:https://www.iteye.com/blog/zapldy-746458