前言
- UDP是无连接协议,即是在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来讲,当一台计算机向另一台计算机发送数据时,发送端不会决定接收方存不存在,就会发出数据,同样接受端在收到数据时,也不会向发送端反馈是否接到数据。
- 本文基于同一局域网下设备交互场景讲解。
- udp本身没有c/s的概念,为了方便区分为客户端和服务端。
- 在android中发送和接收消息都需要在子线程中操作。
发送消息
- 为了保证消息发送成功,规定了服务端接收到消息必须给返回响应数据
- TIMEOUT设置发送消息后等待响应的时间,RETRY_NUM设置重发的次数。
public static void sendMessage(String ip, int port, String data, UDPCallback callback) {
mPool.execute(() -> {
try {
InetAddress inetAddress = InetAddress.getByName(ip);
DatagramSocket client = new DatagramSocket();
client.setSoTimeout(TIMEOUT);
byte[] dataBytes = data.getBytes();
DatagramPacket dataPacket = new DatagramPacket(dataBytes, dataBytes.length, inetAddress, port);
byte[] responseBytes = new byte[1024 * 1024];
DatagramPacket responsePacket = new DatagramPacket(responseBytes, responseBytes.length);
int tries = 0;
boolean receivedResponse = false;
while (!receivedResponse && tries < RETRY_NUM) {
client.send(dataPacket);
try {
client.receive(responsePacket);
String serviceIp = responsePacket.getAddress().getHostAddress();
String response = new String(responseBytes, 0, responsePacket.getLength());
if (callback != null) {
ThreadUtils.runOnUiThread(() -> callback.call(serviceIp, response));
}
receivedResponse = true;
} catch (InterruptedIOException e) {
tries += 1;
Log.e("udp", "client Time out," + (RETRY_NUM - tries) + " more tries...");
}
}
Log.e("udp", "client No response -- give up.");
client.close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
接收消息
- 接收消息receive方法是阻塞的,控制死循环一直接收消息
public static void receiveMessage(int port, boolean repeat, String response, UDPCallback callback) {
isReceiveRunning = true;
mPool.execute(() -> {
try {
byte[] dataBytes = new byte[1024 * 1024];
final DatagramPacket clientPacket = new DatagramPacket(dataBytes, dataBytes.length);
DatagramSocket service = new DatagramSocket(null);
service.setReuseAddress(true);
service.bind(new InetSocketAddress(port));
do {
service.receive(clientPacket);
String clientIp = clientPacket.getAddress().getHostAddress();
String data = new String(dataBytes, 0, clientPacket.getLength());
ThreadUtils.runOnUiThread(() -> callback.call(clientIp, data));
byte[] responseBytes = (response == null ? "default response" : response).getBytes();
DatagramPacket responsePacket = new DatagramPacket(responseBytes, responseBytes.length, clientPacket.getAddress(), clientPacket.getPort());
service.send(responsePacket);
} while (repeat && isReceiveRunning);
service.disconnect();
service.close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
建立连接
- 建立连接逻辑是客户端和服务端绑定不同的端口,客户端发送广播到指定的服务端端口,服务端接收广播发送的消息并响应,双方获取到对应的IP以实现一对一的发送。
public static void sendBroadcast(int port, String data, int interval, UDPCallback callback) {
isSendBroadcastRunning = true;
Runnable runnable = new Runnable() {
@Override
public void run() {
sendMessage(NetworkUtils.getBroadcastIpAddress(), port, data, callback);
if (isSendBroadcastRunning) {
mHandler.postDelayed(this, interval);
}
}
};
mHandler.postDelayed(runnable, interval);
}
public static void sendBroadcast(int port, String data, UDPCallback callback) {
sendMessage(NetworkUtils.getBroadcastIpAddress(), port, data, callback);
}