单播

单播(原文:unicast)是指数据包在计算机网络的传输中,目的地址为单一目标的一种传输方式。它是现今网络应用最为广泛,通常所使用的网络协议或服务大多采用单播传输,例如一切基于TCP的协议。

组播

多播(英语:multicast,台湾又译作多点发送、多点广播或群播,中国大陆又译作组播)是指把信息同时传递给一组目的地址。它使用的策略是最高效的,因为消息在每条网络链路上只需传递一次,且只有在链路分叉的时候,消息才会被复制。

广播

广播(英语:broadcast)是指将信息数据包发往指定网络范围内的所有设备[1]。其发送范围称为“广播域”。

并非所有的计算机网络都支持广播,例如X.25网络和帧中继都不支持广播,而且也没有在“整个互联网范围中”的广播。IPv6亦不支持广播,广播的相应功能由多播代替。

通常来说,广播都是限制在局域网范围内,比如以太网或令牌环网络。因为广播在广域网中可能造成比在局域网中大的多的影响。

UDP 是什么

udp 是用户数据报文协议
特点:

  • 不可靠:
    • 一旦应用程序发给网络的数据发送出去,就不保存备份
  • 使用: dns,TFTP, SNMP

UDP 包的最大长度:
16 位 -> 2字节
2^16 - 1 = 65536 - 1 = 65535 字节长度
自身协议占用32 + 32 = 64 位 = 8字节
65536 - IP头长度(20)- 8 = 65507 (理论)

java 中Udp相关api

  1. DatagramSocket 创建简单的实例,指定端口时,创建监听指定端口的实例。
  2. DatagramPacket packet 用于将byte数组包装为报文,或者将报文拆分为byte数组。
    • 构造函数 DatagramPacket(bytes[], length, InetAddress, port) 依次为数据的byte数组,byte数组的长度,要传输的地址,要传输的端口号
    • packet getAddress 发送者的地址
    • packet getAddress().getHostAddress() 获取发送者的IP地址
    • packet getPort() 获取发送者的端口
    • packet getLength() 获取接收到的数据长度
    • packet getData() 获取接收到的数据
  3. receive(DatagramPacket d) 接收数据
  4. send(DatagramPacket d) 发送数据

java upd 实现单播

服务端监听20000端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

public class UdpServer {
public static void main(String[] args) {
System.out.println("UDPServer started.");
// 接收者,指定端口用于数据接收
DatagramSocket ds = new DatagramSocket(20000);
// 创建存放数据的数组
final byte[] buf = new byte[512];
DatagramPacket receivePackt = new DatagramPacket(buf, buf.length);
// 接收数据
ds.receive();
// 打印收到的信息与发送者的信息
// 发送者的ip地址
String ip = receivePackt.getAddress().getHostAddress();
// 发送者的端口
int port = receivePackt.getPort();
int dataLen = receivePackt.getLength();
String data = new String(receivePackt.getData, 0, dataLen);
System.out.println("UDPServer receive from ip: " + ip + "\t port:" + port + "\tdata:" + data );

// 构建一份回送数据
String responseData = "Receive data with len: " + dataLen;
byte[] responseDataBytes = responseData.getBytes();
// 直接根据发送者构建一份回送信息
DatagramPacket responsePacket = new DatagramPacket(
responseDataBytes,
responseDataBytes.length,
receivePackt.getAddress(),
receivePackt.getPort()
);
ds.send(responsePacket);

//完成
System.out.println(" UDPServer finished");
ds.close();

}
}

客户端向20000端口发送信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

public class UdpClient{
public static void main(String[] args) {
System.out.println(" UdpClient start");
DatagramSocket ds = new Datagramsocket();
// 构建一份发送数据
String requestData = "Hello World";
byte[] requestBytes = requestData.getBytes();
DatagramPacket requestPacket = new DatagramPacket(requestBytes, requestBytes.length);
requestPacket.setAddress(Inet4Address.getLocalHost());
requestPacket.setPort(20000);
// 发送
ds.send(requestPacket);

// 接收server回送的消息
final byte[] buf = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(byte, byte.length);

ds.receive(receivePacket);

// 打印接收到的信息与发送者的信息
// 发送者的IP地址
String ip = receivePack.getAddress().getHostAddress();
// 发送者的端口
int port = receivePack.getPort();

int dataLen = receivePack.getLength();
String data = new String(receivePack.getData(), 0, dataLen);
System.out.println("UDPSearcher receive from ip: " + ip + "\t port:" + port + "\tdata:" + data );
//完成
System.out.println(" UDPSearcher finished");
ds.close();
}
}

java upd 广播

实现思路:
A -> B 、C 、D
数据流向: A 监听30000 端口,B,C,D监听20000端口 ,
A 向20000 端口发送广播后,B、C、D接收到消息(消息中含有需要回复的端口号)后,向30000端口进行回复。

服务端监听20000端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class Server{

public static void main(String[] args) throws IOException {
// 生成唯一标识
String sn = UUID.randomUUID().toString();

Send send = new Send(sn);
send.start();

// 读取任意
System.in.read();
send.exit();
}

private static class Send extends Thread {
private final String sn;
private boolean done = false;
private DatagramSocket ds = null;

public Provider(String sn) {
this.sn = sn;
}

@Override
public void run() {
System.out.println("UDPProvider started.");
try{
ds = new DatagramSocket(20000);
while(!done) {
final byte[] buf = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
ds.receive(receivePacket);

// 打印接收到的信息与发送者的信息
// 发送者的IP地址
String ip = receivePack.getAddress().getHostAddress();
// 发送者的端口
int port = receivePack.getPort();

int dataLen = receivePack.getLength();
String data = new String(receivePack.getData(), 0, dataLen);
System.out.println("UDPProvider receive from ip: " + ip + "\t port:" + port + "\tdata:" + data);

int responsePort = MessageCreator.parsePort(data);
if (responsePort != -1) {
// 构建一份回送数据
String responseData = MessageCreator.buildWithSn(sn);
byte[] responseDataBytes = responseData.getBytes();
// 直接根据发送者构建一份回送信息
DatagramPacket responsePacket = new DatagramPacket(responseDataBytes,
responseDataBytes.length,
receivePack.getAddress(),
responsePort);

ds.send(responsePacket);
}

}
} catch(Exception ignore) {

} finally {
close();
}
System.out.println(" UDPProvider finished");
}

private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}

void exit() {
done = true;
close();
}

}

}

口令的创建与解析
根据口令对client信息中的端口号进行解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

public class MessageCreator {
// B C D 回送暗号
private static final String SN_HEADER = "收到暗号,我是(SN) :";

// 需要其他机器回送的本端口号
private static final String PORT_HEADER = "这是暗号,请回电端口(port):";

public static String buildWithPort(int port) {
return PORT_HEADER + port;
}

public static int parsePort(String data) {
if (data.startsWith(PORT_HEADER)) {
return Integer.parseInt(data.substring(PORT_HEADER.length()));
}
return -1;
}

public static String buildWithSn(String sn) {
return SN_HEADER + sn;
}

public static String parseSn(String data) {
if(data.startsWith(SN_HEADER)) {
return data.substring(SN_HEADER.length());
}

return null;
}

}

udp广播的Client

监听A、B、C的回拨端口30000,后向A、B、C端口发送信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

class Client{

private static final int LISTEN_PORT = 30000;

public static void main(String[] args) {
Listener listener = listen();
sendBroadCast();

System.in.read();
List<Device> devicesAndClose = listener.getDevicesAndClose();
for (Device device : devicesAndClose) {
System.out.println("device:" + device.toString());
}
}

private static Listener listen() throws Exception{
System.out.println("开始监听");
CountDownLatch countDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTEN_PORT, countDownLatch);
listener.start();

countDownLatch.await();

return listener;
}

private static class Device {
private String sn;
private int port;
private String ip;

public Device(String sn, int port, String ip) {
this.sn = sn;
this.port = port;
this.ip = ip;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

public String getIp() {
return ip;
}

public void setIp(String ip) {
this.ip = ip;
}

public String getSn() {
return sn;
}

public void setSn(String sn) {
this.sn = sn;
}

@Override
public String toString() {
return "Device{" +
"sn='" + sn + '\'' +
", port=" + port +
", ip='" + ip + '\'' +
'}';
}
}

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) {
this.listenPort = listenPort;
this.countDownLatch = countDownLatch;
}

@Override
public void run() {
try {
countDownLatch.countDown();
ds = new DatagramSocket(listenPort);
while (!done) {
// 构建接收实体
final byte[] buf = new byte[512];
DatagramPacket receivePack = new DatagramPacket(buf, buf.length);
ds.receive(receivePack);
// 打印接收到的信息与发送者的信息
// 发送者的IP地址
String ip = receivePack.getAddress().getHostAddress();
int port = receivePack.getPort();
int dataLen = receivePack.getLength();
String data = new String(receivePack.getData(), 0, dataLen);

System.out.println("UDP Searcher receive from ip:" + ip + "\t port:" + port + "\t data" + data);

String sn = MessageCreator.parseSn(data);
if (sn != null) {
Device device = new Device(sn, port, ip);
devices.add(device);
}
}
} catch (Exception ignored) {

} 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;
}
}


public void broadCast() {
System.out.println("UDPSearcher sendBroadcast started");
DatagramSocket ds = new DatagramSocket();
String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
byte[] requestDataBytes = requestData.getBytes();
DatagramPacket requestPacket = new DatagramPacket(requestDataBytes, requestDataBytes.length);
requestPacket.setAddress(Inet4Address.getByName("255,255,255,2555"));
requestPacket.setPort(20000);
ds.send();
ds.close();
System.out.println("UpdSearcher sendBroadcast finish");
}

}