(四) Socket——UDP快速入门

it2025-01-15  15

1、UDP是什么

Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议,又称用户数据报文协议,基于报文的协议(不同于TCP面向链接的协议)是一种简单的面向数据报的传输层协议,正式规范RFC 768,提供面向事务的简单不可靠信息传送服务

为什么不可靠

一旦把应用程序发给网络层的数据发送出去,就不保留数据备份UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)发送端是产生数据,在接收端从网络中抓取数据特点:结构简单、无校验、速度快、容易丢包、可广播

UDP能做什么

DNS、TFTP、SNMP视频、音频、普通数据(无关紧要数据)

2、UDP核心API讲解

API-DatagramSocket

此类表示用来发送和接收数据包的套接字,数据报套接字是包投递服务的发送或接收点负责发送某一个UDP包,或者接收UDP包不同于TCP,UDP并没有合并到Socket API中(UDP中没有区分客户端和服务器,DatagramSocket即是客户端也是服务端,可以发送也可以接收,不需要连接)DatagramSocket()创建简单实例,不指定端口与IP(使用它发送,他会自动赋予本地可用端口进行发送数据)DatagramSocket( int port)创建监听固定端口的实例DatagramSocket( int port,InetAddress localAddr)创建固定端口制定IP的实例

3、UDP单播、广播、多播

广播地址

255.255.255.255为受限广播地址c网广播地址一般为:xxx.xxx.xxx.255D类广播地址为多播预留广播地址运算:子网掩码、网络地址、广播地址的计算 - yangyang的专栏 - 博客 https://blog.csdn.net/u014465934/article/details/81146443如何计算网络地址和广播地址 - lzh657083979的博客 - 博客 https://blog.csdn.net/lzh657083979/article/details/77606217

4、局域网搜索案——A向B发送信息

4.1、实现UDP接收消息并回送功能

UDP提供者代码:

import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; /** * UDP提供者,用于提供服务 */ public class UDPprovider { public static void main(String[] args) throws IOException { System.out.println("UDPProvider started."); //作为接受者,制定一个端口用于数据接收 DatagramSocket ds = new DatagramSocket(20000); //构建接受实体 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("UDPProvider receive form ip:"+ip+"\tport:"+port+"\tdata:"+data); //构建一份回送数据 String responseData="\nreceive data with len:"+dataLen; //构建回送消息的实体 byte[] responseDataBytes=responseData.getBytes(); //直接根据发送者构建一份回送信息 DatagramPacket responsePacket =new DatagramPacket(responseDataBytes, responseDataBytes.length, receivePack.getAddress(), receivePack.getPort()); ds.send(responsePacket); //完成 System.out.println("UDPProvider finished."); ds.close(); } }

UDP搜索者代码:

import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; /** * UDP搜索者用于搜索支持方 */ public class UDPsearcher { public static void main(String[] args) throws IOException { System.out.println("UDPseacher started."); //作为搜索方,让系统自动分配端口 DatagramSocket ds = new DatagramSocket(); //构建一份请求数据 String requestData="helloworld!"; //构建回送消息的实体 byte[] requestDataBytes=requestData.getBytes(); //直接构建packet DatagramPacket requestPacket =new DatagramPacket(requestDataBytes,requestDataBytes.length); //本机端口20000 requestPacket.setAddress(InetAddress.getLocalHost()); requestPacket.setPort(20000); //发送 ds.send(requestPacket); //构建接受实体 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("UDPseacher receive form ip:"+ip+"\tport:"+port+"\tdata:"+data); //完成 System.out.println("UDPseacher finished."); ds.close(); } }

5、局域网搜索案例——A向局域网内所有设备发送信息

UDP局域网广播发送的实现UDP局域网回送消息的实现A作为提供者,B作为接收者

UDP提供者,用于提供服务

import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.util.UUID; /** * UDP提供者,用于提供服务 */ 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;//判断是否已完成,默认没有完成 private DatagramSocket ds=null;//用于循环支持 public Provider(String sn) { super(); this.sn = sn; } //Intellij Idea get/set/Override方法快捷键Alt+Insert,或直接ctrl+o @Override public void run() { super.run(); System.out.println("UDPProvider started."); try { //监听20000端口 ds = new DatagramSocket(20000); 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("UDPProvider receive form ip:" + ip + "\tport:" + 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 e){ //这个异常可以忽略 }finally { close(); } //完成 System.out.println("UDPProvider finished."); } private void close(){//socket进行结束操作 if(ds!=null){ ds.close(); ds=null; } } /** * 提供结束 */ void exit(){ done=true; close(); } } }

消息创建者,具备两个口令,一个搜索口令,一个回送口令

public class MessageCreator {//消息创建者,具备两个口令,一个搜索口令,一个回送口令 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搜索者用于搜索支持方

import java.io.IOException; import java.net.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * UDP搜索者用于搜索支持方 */ public class UDPsearcher { private static final int LISTEN_PORT=30000;//定义回送监听端口号 public static void main(String[] args) throws IOException, InterruptedException { System.out.println("UDPseacher started."); Lisener listener = listen();//开始一个监听 sendBroadcast();//监听之后发送一个广播 /* //作为搜索方,让系统自动分配端口 DatagramSocket ds = new DatagramSocket(); //构建一份请求数据 String requestData="helloworld!"; //构建回送消息的实体 byte[] requestDataBytes=requestData.getBytes(); //直接构建packet DatagramPacket requestPacket =new DatagramPacket(requestDataBytes,requestDataBytes.length); //本机端口20000 requestPacket.setAddress(InetAddress.getLocalHost()); requestPacket.setPort(20000); //发送 ds.send(requestPacket); //构建接受实体 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("UDPseacher receive form ip:"+ip+"\tport:"+port+"\tdata:"+data);*/ //ds.close(); //读取任意键盘信息后可以退出 System.in.read(); List<Device> devices = listener.getDevicesAndClose();//结束之前拿到设备信息,从监听器中拿我们的设备信息 for (Device device : devices) {//拿到设备之后,打印设备信息 System.out.println("Device:"+device.toString()); } //完成 System.out.println("UDPseacher finished."); } //监听方法 private static Lisener listen() throws InterruptedException {//监听方法 System.out.println("UDPseacher start listen."); CountDownLatch countDownLatch=new CountDownLatch(1); Lisener lisener=new Lisener(LISTEN_PORT,countDownLatch); lisener.start();//启动操作 countDownLatch.await();//等待启动完成 return lisener; } //发送广播的方法 public static void sendBroadcast() throws IOException {//发送广播的方法 System.out.println("UDPseacher sendBroadcast started."); //作为搜索方,让系统自动分配端口 DatagramSocket ds = new DatagramSocket(); //构建一份请求数据 String requestData=MessageCreator.buildWithPort(LISTEN_PORT);//构建一个发送包 //构建回送消息的实体 byte[] requestDataBytes=requestData.getBytes(); //直接构建packet DatagramPacket requestPacket =new DatagramPacket(requestDataBytes,requestDataBytes.length); //广播地址,20000端口 requestPacket.setAddress(InetAddress.getByName("255.255.255.255")); requestPacket.setPort(20000); //发送 ds.send(requestPacket); ds.close(); //完成 System.out.println("UDPseacher sendBroadcast finished."); } private static class Device{//设备类信息 final int port;//端口 final String ip; final String sn; //三者都是必须要的东西,因此加上final private Device(int port, String ip, String sn) { this.port = port; this.ip = ip; this.sn = sn; } //同时构建一个tostring方法:快捷键Alt+Insert选择tostring()再点击OK @Override public String toString() { return "Device{" + "port=" + port + ", ip='" + ip + '\'' + ", sn='" + sn + '\'' + '}'; } } private static class Lisener extends Thread{ private final int listenPort;//需要传入的端口号 private final CountDownLatch countDownLatch;//让外面感知到已经开始监听了 private final List<Device> devices=new ArrayList<>();//设备链表 private boolean done=false;//结束符,默认为false private DatagramSocket ds=null;//搜索者 public Lisener(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 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("UDPseacher receive form ip:"+ip+"\tport:"+port+"\tdata:"+data); //解析操作,解析sn String sn=MessageCreator.parseSn(data); if(sn!=null){ Device device=new Device(port,ip,sn);//传入新设备端口号、ip及sn信息 devices.add(device);//sn解析成功,设备链表增加一个新的设备 } } }catch (Exception e){ }finally { close(); } System.out.println("UDPseacher listener finished"); } private void close(){ if(ds!=null){ ds.close(); ds=null; } } List<Device>getDevicesAndClose(){//拿并关闭的操作 done=true; close(); return devices; } } }

运行结果: 首先要打开提供方,再打开搜索方 在搜索方输入任意字符结束搜索和提供方:

最新回复(0)