UDP辅助TCP实现点对点传输案例:
服务器判断收到的广播是不是咋们需要处理的,是的话,服务器会回送这个广播到对应的端口,对应的地址上面去,当这个回送的时候,收到的这个udp包就包含了端口号以及ip地址,
UDP搜索端口与IP
1、构建基础口令消息
2、局域网广播口令消息(指定端口)
3、接受指定端口回送消息,从而得到ip和端口号
首先发送一个广播,局域网中所有设备都会收到这样一个广播,收到的是感兴趣的就会回送这个消息,所有感兴趣的设备都会回送这个消息,
UDP搜索取消实现
1、异步线程接收回送消息
2、异步线程等待完成
3、关闭等待,终止线程等待。
public class UDPSearcher {
private static final int LISTEN_PORT = UDPConstants.PORT_CLIENT_RESPONSE;
public static ServerInfo searchServer(int timeout) {
System.out.println("UDPSearcher Started.");
// 成功收到回送的栅栏
CountDownLatch receiveLatch = new CountDownLatch(1);
Listener listener = null;
try {
listener = listen(receiveLatch);
sendBroadcast();
receiveLatch.await(timeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
}
// 完成
System.out.println("UDPSearcher Finished.");
if (listener == null) {
return null;
}
List<ServerInfo> devices = listener.getServerAndClose();
if (devices.size() > 0) {
return devices.get(0);
}
return null;
}
private static Listener listen(CountDownLatch receiveLatch) throws InterruptedException {
System.out.println("UDPSearcher start listen.");
CountDownLatch startDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTEN_PORT, startDownLatch, receiveLatch);
listener.start();
startDownLatch.await();
return listener;
}
private static void sendBroadcast() throws IOException {
System.out.println("UDPSearcher sendBroadcast started.");
// 作为搜索方,让系统自动分配端口
DatagramSocket ds = new DatagramSocket();
// 构建一份请求数据
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
// 头部
byteBuffer.put(UDPConstants.HEADER);
// CMD命名
byteBuffer.putShort((short) 1);
// 回送端口信息
byteBuffer.putInt(LISTEN_PORT);
// 直接构建packet
DatagramPacket requestPacket = new DatagramPacket(byteBuffer.array(),
byteBuffer.position() + 1);
// 广播地址
requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
// 设置服务器端口
requestPacket.setPort(UDPConstants.PORT_SERVER);
// 发送
ds.send(requestPacket);
ds.close();
// 完成
System.out.println("UDPSearcher sendBroadcast finished.");
}
private static class Listener extends Thread {
private final int listenPort;
private final CountDownLatch startDownLatch;
private final CountDownLatch receiveDownLatch;
private final List<ServerInfo> serverInfoList = new ArrayList<>();
private final byte[] buffer = new byte[128];
private final int minLen = UDPConstants.HEADER.length + 2 + 4;
private boolean done = false;
private DatagramSocket ds = null;
private Listener(int listenPort, CountDownLatch startDownLatch, CountDownLatch receiveDownLatch) {
super();
this.listenPort = listenPort;
this.startDownLatch = startDownLatch;
this.receiveDownLatch = receiveDownLatch;
}
@Override
public void run() {
super.run();
// 通知已启动
startDownLatch.countDown();
try {
// 监听回送端口
ds = new DatagramSocket(listenPort);
// 构建接收实体
DatagramPacket receivePack = new DatagramPacket(buffer, buffer.length);
while (!done) {
// 接收
ds.receive(receivePack);
// 打印接收到的信息与发送者的信息
// 发送者的IP地址
String ip = receivePack.getAddress().getHostAddress();
int port = receivePack.getPort();
int dataLen = receivePack.getLength();
byte[] data = receivePack.getData();
boolean isValid = dataLen >= minLen
&& ByteUtils.startsWith(data, UDPConstants.HEADER);
System.out.println("UDPSearcher receive form ip:" + ip
+ "\tport:" + port + "\tdataValid:" + isValid);
if (!isValid) {
// 无效继续
continue;
}
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, UDPConstants.HEADER.length, dataLen);
final short cmd = byteBuffer.getShort();
final int serverPort = byteBuffer.getInt();
if (cmd != 2 || serverPort <= 0) {
System.out.println("UDPSearcher receive cmd:" + cmd + "\tserverPort:" + serverPort);
continue;
}
String sn = new String(buffer, minLen, dataLen - minLen);
ServerInfo info = new ServerInfo(serverPort, ip, sn);
serverInfoList.add(info);
// 成功接收到一份
receiveDownLatch.countDown();
}
} catch (Exception ignored) {
} finally {
close();
}
System.out.println("UDPSearcher listener finished.");
}
private void close() {
if (ds != null) {
ds.close();
ds = null;
}
}
List<ServerInfo> getServerAndClose() {
done = true;
close();
return serverInfoList;
}
}
}
public class TCPClient {
public static void linkWith(ServerInfo info) throws IOException {
Socket socket = new Socket();
// 超时时间
socket.setSoTimeout(3000);
// 连接本地,端口2000;超时时间3000ms
socket.connect(new InetSocketAddress(Inet4Address.getByName(info.getAddress()), info.getPort()), 3000);
System.out.println("已发起服务器连接,并进入后续流程~");
System.out.println("客户端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort());
System.out.println("服务器信息:" + socket.getInetAddress() + " P:" + socket.getPort());
try {
// 发送接收数据
todo(socket);
} catch (Exception e) {
System.out.println("异常关闭");
}
// 释放资源
socket.close();
System.out.println("客户端已退出~");
}
private static void todo(Socket client) throws IOException {
// 构建键盘输入流
InputStream in = System.in;
BufferedReader input = new BufferedReader(new InputStreamReader(in));
// 得到Socket输出流,并转换为打印流
OutputStream outputStream = client.getOutputStream();
PrintStream socketPrintStream = new PrintStream(outputStream);
// 得到Socket输入流,并转换为BufferedReader
InputStream inputStream = client.getInputStream();
BufferedReader socketBufferedReader = new BufferedReader(new InputStreamReader(inputStream));
boolean flag = true;
do {
// 键盘读取一行
String str = input.readLine();
// 发送到服务器
socketPrintStream.println(str);
// 从服务器读取一行
String echo = socketBufferedReader.readLine();
if ("bye".equalsIgnoreCase(echo)) {
flag = false;
} else {
System.out.println(echo);
}
} while (flag);
// 资源释放
socketPrintStream.close();
socketBufferedReader.close();
}
}