Socket网络编程(二)——UDP快速入门

目录

  • UDP相关概念
    • UDP是什么
    • 为什么不可靠
    • UDP能做什么
    • UDP包最大长度
  • UDP单播、广播、多播概念
    • 1. 单播、广播、多播模型图
    • 2. ip地址分类
    • 3. 子网掩码的作用:
    • 4. 广播地址
    • 5. 网段划分
    • 6. 变长子网掩码
  • UDP核心API
    • API-DatagramSocket
      • Api方法(构造方法)
    • API-DatagramPacket
      • API-DatagramPacket(常用方法)
    • 局域网搜索案例

UDP相关概念

UDP是什么

  • 英语:User Datagram Protocol,缩写为UDP
  • 一种用户数据报协议,又称用户数据报文协议
  • 是一个简单的面向数据报的传输层协议,正式规范为RFC 768
  • 用户数据协议、非连接协议(不可靠协议)

为什么不可靠

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

UDP能做什么

  • DNS(域名系统)、TFTP(简单文件传输协议)、SNMP(简单网络管理协议)
  • 视频、音频、普通数据(无关紧要数据)

UDP包最大长度

  • 16位-> 2字节存储长度信息
  • 2个16-1 = 64K-1 = 65536-1 = 65535
  • 自身协议占用:32+32位=64位=8字节
  • 65535-8 = 65507 byte

20240129-162937-0z.png

UDP单播、广播、多播概念

1. 单播、广播、多播模型图

20240131-095225-YI.png

2. ip地址分类

20240131-101516-Kt.png

IP地址分以下五类:

A类 : 从1.0.0.0 到126.255.255.255 ,适用于大型网络B类 : 从128.0.0.0到191.255.255.255 ,适用于中型网络C类 : 从192.0.0.0到223.255.255.255 ,适用于小型网络D类 : 从224.0.0.0到239.255.255.255,用于组播E类 : 从240.192.0.0.0到255.255.255.255,用于科研保留A,B,C为常用类别。其中127.x.x.x段地址空间是被保留的回环地址网络部分(网络地址)和主机部分(主机地址)IP地址由2部分组成:网络部分+主机部分IP地址=网络地址+主机地址

3. 子网掩码的作用:

子网掩码不能单独存在,它必须结合IP地址一起使用。子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址两部分。
通俗的说,就是用来分割子网和区分那些ip是同一个网段的,那些不是同一网段的。

①通过子网掩码,就可以判断两个IP在不在一个局域网内部。②子网掩码可以看出有多少位是网络号,有多少位是主机号③其对应的IP地址中网络地址的所有位置都为1,对应于主机地址的所有位置都为0。每一类IP地址都有默认的子网掩码对于A类地址来说,默认的子网掩码是255.0.0.0;对于B类地址来说,默认的子网掩码是255.255.0.0;对于C类地址来说,默认的子网掩码是255.255.255.0。

网络地址就是:把IP地址转成二进制和子网掩码进行与运算
与位运算:0与任何数运算为0,1与1运算为1

示例:一个主机的IP地址是202.112.14.137,掩码是255.255.255.224,要求计算这个主机所在网络的网络地址和广播地址

子网掩码255.255.255.224 转二进制:11111111.11111111.11111111.11100000其中网络位部分是27位,主机位部分是5位IP地址202.112.14.137转二进制:11001010 01110000 00001110 10001001IP地址&子网掩码(进行与运算)11001010 01110000 00001110 1000100111111111 11111111 11111111 1110000011001010 01110000 00001110 10000000注:与位运算:0与任何数运算为0,1与1运算为1结果为:202.112.14.128
即网络地址为:202.112.14.128网络地址的主机位全部变成1,10011111,即159 
即广播地址为:202.112.14.159

判断两个IP地址是否为同一个网段,就是分别根据对应的IP地址和子网掩码计算出对应的网络地址,网络地址相同就是在同一个网段,不相同就不在同一个网段,不在同一个网段的ip地址之间无法进行直接通信(无法收到广播消息)。

4. 广播地址

广播地址(Broadcast Address)是专门用于同时向网络中所有工作站进行发送的一个地址。
在使用TCP/IP 协议的网络中,主机标识段host ID(主机位) 为全1 的IP 地址为广播地址,广播的分组传送给host ID段所涉及的所有计算机。
例如,对于10.1.1.0 (255.255.255.0 )网段,其广播地址为10.1.1.255 (255 即为2 进制的11111111 ),当发出一个目的地址为10.1.1.255 的分组(封包)时,它将被分发给该网段上的所有计算机。

5. 网段划分

常见数字:

  2,  4,  8, 16, 32, 64,128,2561,  3,  7, 14, 31, 63,127,255
255,254,252,248,240,224,192,128

大部分的情况下,我们网络中只要不超过254台主机的的话,子网掩码都可以设置成255.255.255.0。
那么超过了254台主机的话怎么办呢?600个ip地址的项目如何设置子网掩码呢?
可以划分vlan,也可以设置成一个大网段。
比如下面这个例子:

假设监控网络中有600个点位,我们现在也不想把它划分vlan(实际项目中大部分是会划分vlan的),假设只想用一个大网段把这600个点位分配ip地址,如何设置ip地址,如何确实子网掩码?

分析:首先我们知道600个点位,可以使用3个254个ip地址段来分配。

可以使用ip段一:192.168.0.1——192.168.0.254ip段二:192.168.1.1——192.168.1.254ip段三:192.168.2.1——192.168.2.254

每个网段有254个ip地址,完全够600个点位用的。
那么问题来了,如果要使这三个ip段在同一个网段内,那么这个大网段共同的子网掩码是多少呢?

将ip地址192.168.1.1转换为二进制11000000 10101000 00000000 00000001将ip地址192.168.1.1转换为二进制11000000 10101000 00000001 00000001将ip地址192.168.2.1转换为二进制11000000 10101000 00000010 00000001转换成了二进制,可以看得出,三个ip段的二进制前面22位的是不变的,那么可以将他们表示成:ip段一:192.168.0.1/22ip段二:192.168.1.1/22ip段三:192.168.2.1/22这种192.168.1.x/22形式的ip地址相信大家平时都见过,就是已经告诉了子网掩码了。也就是说他们共同的子网掩码二进制前面22个都是1。11111111 11111111 11111100 00000000转换成十进制,那就是255.255.252.0,所以他们共同的子网掩码就是255.255.252.0。

注意:子网掩码越精准越好,范围不要太大,不要统一写255.255.0.0,太大的范围在分配ip地址后,容易出现一些掉线故障,实际项目中,不用做这些复杂的运算,熟悉原理过后,看到了IP地址数量基本上就可以写出来。

所以通常:

网段ip地址低于254个,子网掩码可以设置成255.255.255.0网段ip地址低于600个,子网掩码可以设置成255.255.252.0网段ip地址低于1000个,子网掩码可以设置成255.255.248.0

6. 变长子网掩码

定义:变长子网掩码(VLSM)是指一个网络可以用不同的掩码进行配置,将一个网络(网络地址)划分为多个子网,提供更多的灵活性,同时保证在每个子网能够有足够数量的主机。

好处:可变长子网掩码缓解了使用缺省子网掩码导致的地址浪费问题;同时也为企业网络提供了更为有效的偏址方案。

原理:其实变长子网掩码和子网划分类似,一个ip地址的网络位,是通过ip地址与子网掩码进行“与”运算得到的。我们以C类ip地址192.168.1.1为例,C类ip地址的缺省子网掩码为:255.255.255.0,转化为二进制为11111111.11111111.11111111.00000000,可用主机位为2^8-2=254个。
所以可以通过向最后8位借位的方式,将整个192.168.1.0网段划分出更多的网段。但是为什么借位就能划分出更多的网段呢?
通过计算的方式,可以得到结果:

如果我们借1位会出现以下情况:借1位后,子网掩码变成了11111111.11111111.11111111.10000000
255.255.255.128(注:借位时只能从最高位开始借。)此时,我们通过ip地址与子网掩码进行“与”运算的方式,计算192.168.1.1与192.168.1.129的网络位,如下图:

20240227-181330-fi.png
得出的结果转化为10进制 为:192.168.1.0与192.168.1.128,计算出来的结果不同,意味着这两个ip在修改子网掩码后,已经成为两个不同网段的ip地址了。

我们知道了子网掩码借位可以增加网段,相应的主机位会相应减少,具体是多少呢?又如何计算呢?
总结如下:

每当子网掩码借n位,就会将原网段划分为2^n个网段,主机位为2^(8-n) -2  (减2为减去该网段的网络地址与主机位)。例如:借1位,原网段就会被划分为2^1=2个网段,主机位为2^(8-1)-2=126个。此时,我们就可以根据需要的网段,需要的设备进行网段划分了。

举个例子:

主机一:192.168.124.7,子网掩码:255.255.255.192
主机二:192.168.124.100,子网掩码:255.255.255.192
通过计算网络地址,得到广播地址为:
主机一广播地址:192.168.124.63
主机二广播地址:192.168.124.127

因此该两个主题无法广播通信。

UDP核心API

API-DatagramSocket

  • 用于接收与发送UDP的类
  • 负责发送某一个UDP包,或者接收UDP包
  • 不同于TCP,UDP并没有合并到Socket API中
  • 因此UDP发送消息没有服务端和客户端,它既是服务端也是客户端

Api方法(构造方法)

  • DatagramSocket()创建简单实例,不指定端口与IP
  • DatagramSocket(int port)创建监听固定端口的实例
  • DatagramSocket(int port, InetAddress localAddr)创建固定端口指定IP的实例
  • receive(DatagramPacket d):接收
  • send(DatagramPacket d):发送
  • setSoTimeout(int timeout):设置超时,毫秒
  • close():关闭、释放资源

API-DatagramPacket

  • 用于处理报文
  • 将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组
  • 是UDP的发送实体,也是接收实体

API-DatagramPacket(常用方法)

  • DatagramPacket(byte[] buf, int offset, int length, InetAddressaddress, int port)
    前面3个参数指定buf的使用区间
    后面2个参数指定目标机器地址与端口

  • DatagramPacket(byte[] buf, int length, SocketAddressaddress)
    前面3个参数指定buf的使用区间
    SocketAddress相当于InetAddress+Port

  • setData(byte[] buf, int offset, int length)

  • setData(byte[] buf)

  • setLength(int length)

  • getData(),getOffset(),getLength()

  • setAddress(InetAddress iaddr)、setPort(int iport)

  • getAddress()、getPort()

  • setSocketAddress(SocketAddress address)

  • getSocketAddress()

局域网搜索案例

需求:
UDP接收消息并回送功能实现
UDP局域网广播发送实现
UDP局域网回送消息实现

步骤如下

  1. 新建一个消息生产和解析类
package cn.kt.SocketDemoL3;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;}}
  1. 新建消息提供者客户端
package cn.kt.SocketDemoL3;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();// 读取任意键盘信息后可以退出//noinspection ResultOfMethodCallIgnoredSystem.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;}@Overridepublic 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 ignored) {} finally {close();}// 完成System.out.println("UDPProvider Finished.");}private void close() {if (ds != null) {ds.close();ds = null;}}/*** 提供结束*/void exit() {done = true;close();}}}
  1. 新建消息搜寻者客户端
package cn.kt.SocketDemoL3;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
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("UDPSearcher Started.");Listener listener = listen();sendBroadcast();// 读取任意键盘信息后可以退出//noinspection ResultOfMethodCallIgnoredSystem.in.read();List<Device> devices = listener.getDevicesAndClose();for (Device device : devices) {System.out.println("Device:" + device.toString());}// 完成System.out.println("UDPSearcher Finished.");}private static Listener listen() throws InterruptedException {System.out.println("UDPSearcher start listen.");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[] requestDataBytes = requestData.getBytes();// 直接构建packetDatagramPacket 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("UDPSearcher sendBroadcast finished.");}// 设备实体类private static class Device {final int port;final String ip;final String sn;private Device(int port, String ip, String sn) {this.port = port;this.ip = ip;this.sn = sn;}@Overridepublic 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;}@Overridepublic 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("UDPSearcher receive form ip:" + ip+ "\tport:" + port + "\tdata:" + data);String sn = MessageCreator.parseSn(data);if (sn != null) {Device device = new Device(port, ip, sn);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;}}
}

运行结果:
提供者发送暗号
20240228-133900-FT.png

搜索者收到暗号
20240228-133939-2b.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/496551.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx实现http反向代理及负载均衡

目录 一、代理概述 1、代理概念 1.1 正向代理&#xff08;Forward Proxy&#xff09; 1.2 反向代理&#xff08;Reverse Proxy&#xff09; 1.3 正向代理与反向代理的区别 2、同构代理与异构代理 2.1 同构代理 2.2 异构代理 2.3 同构代理与异构代理的区别 二、四层代…

计算机网络-网络互连和互联网(四)

1.TCP协议&#xff1a; 传输控制协议&#xff0c;面向字节流按顺序连接&#xff0c;可靠&#xff0c;全双工&#xff0c;可变滑动窗口&#xff0c;缓冲累积传送。协议号为6。下面是TCP段&#xff08;段头&#xff09;&#xff0c;TCP头&#xff08;传输头&#xff09;&#xf…

探索AI视频模型的无限可能:OpenAI的Sora引领创新浪潮

文章目录 &#x1f4d1;前言一、技术解析二、应用场景三、未来展望四、伦理与创意五、用户体验与互动&#x1f324;️总结 &#x1f4d1;前言 随着人工智能技术的蓬勃发展&#xff0c;AI视频模型正逐渐成为科技领域的新宠。在这个变革的浪潮中&#xff0c;OpenAI推出的首个AI视…

【C++那些事儿】深入理解C++类与对象:从概念到实践(上)| 揭开this指针的神秘面纱

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C那些事儿 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 1. 面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符及封装4.1 访问限定符…

使用 kubeadm 部署k8s集群

一、所有节点系统初始化 1、常规初始化 2、内核版本升级以及内核限制文件参数修改 还可以考虑将旧版本的内核卸载 二、准备nginx负载均衡器和keepalived nginx四层代理&#xff1a; keepalived配置&#xff1a; nginx检测脚本&#xff1a; 三、所有节点部署docker&#xff0c…

接口测试实战--mock测试、日志模块

一、mock测试 在前后端分离项目中,当后端工程师还没有完成接口开发的时候,前端开发工程师利用Mock技术,自己用mock技术先调用一个虚拟的接口,模拟接口返回的数据,来完成前端页面的开发。 接口测试和前端开发有一个共同点,就是都需要用到后端工程师提供的接口。所以,当…

记一次生产jvm oom问题

前言 jvm添加以下参数&#xff0c;发生OOM时自动导出内存溢出文件 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/opt 内存分析工具&#xff1a; MAT, 下载地址&#xff1a;Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation&#xff0c; 注意工具地址…

新款极氪001“卷”疯了,加量不加价友商都别活了

文 | AUTO芯球 作者 | 雷歌 我惊呆了朋友们。 昨晚的极氪001新款发布会我全程在看&#xff0c;上个厕所都没舍得挪窝&#xff0c;生怕错过了新车爆点。 要不是极氪CEO安聪慧曝光&#xff0c;极氪001这个隐形的王者&#xff0c;还完全隐没在你我的视野里。 什么秘密呢&…

《TCP/IP详解 卷一》第9章 广播和本地组播

目录 9.1 引言 9.2 广播 9.2.1 使用广播地址 9.2.2 发送广播数据报 9.3 组播 9.3.1 将组播IP地址转换为组播MAC地址 9.3.2 例子 9.3.3 发送组播数据报 9.3.4 接收组播数据报 9.3.5 主机地址过滤 9.4 IGMP协议和MLD协议 9.4.1 组成员的IGMP和MLD处理 9.4.2 组播路由…

Python——Tchisla求解器(暴力搜索法)

Tchisla简介 最近玩到一个挺有意思的数字解密小游戏《Tchisla》&#xff0c;其规则类似算24点&#xff0c;也是利用一些数学运算和初始数字计算出目标数字&#xff0c;与算24点不同的是&#xff0c;Tchisla允许不限次数地使用一种初始数字&#xff08;1~9&#xff09;&#xf…

【Qt 学习之路】使用 cmake 在Windows上 编译 ZeroMQ

文章目录 1、概述2、编译2.1、用 Visual Studio 的解决方案方式2.1.1、找到 Builds 文件夹2.1.2、查看 deprecated-msvc 下的 libzmq.sln 文件2.1.3、使用 Visual Studio 打开 libzmq.sln 解决方案2.1.4、修改 libzmq.import.props 文件2.1.5、编译生成 2.2、用 C 的cmake方式2…

C语言中如何进行内存管理

主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《C语言》 C语言是一种强大而灵活的编程语言&#xff0c;但与其他高级语言不同&#xff0c;它要求程序员自己负责内存的管理。正确的内存管理对于程序的性能和稳定性至关重要。 一、引言 C 语言是一门广泛使用的编程语…