【Linux C | 网络编程】广播概念、UDP实现广播的C语言例子

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍广播概念、UDP实现广播的C语言例子 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:2024-03-06 00:10:30

本文未经允许,不得转发!!!

目录

  • 🎄一、广播概述
  • 🎄二、广播地址
  • 🎄三、UDP单播 和 UDP广播 的比较
    • ✨3.1 UDP单播过程
    • ✨3.2 UDP广播过程
  • 🎄四、UDP实现广播的例子
  • 🎄五、总结


在这里插入图片描述

🎄一、广播概述

在网络编程中,有三种常见的通信方式:单播、广播、多播(组播),这三种方式对比如下表:

类型IPv4IPv6TCPUDP所标识接口数递送到的接口数
单播支持支持支持支持一个一个
广播支持支持全体全体
多播可选支持支持一组整个组

广播的概念
广播是允许一台主机本地子网内所有主机 发送消息的一种通信方式。发送的广播分组会去往子网上的所有主机,包括发送者自身。

  • 广播只支持IPv4,不支持IPv6:使用广播的IPv4应用程序一旦移植到IPv6,就必须使用多播重新编写;
  • 广播支持UDP或原始IP,不支持TCP。

广播的用途

  • 资源发现:知道子网中有我们想找的资源,但不知道其具体IP地址,可以往该子网中发一个广播,该资源主机收到广播后处理并回复,非资源主机不处理。举例:NVR发送广播搜索局域网内的IPC。
  • 减少分组流通。

广播的缺点
子网内未参与相应广播的所有主机也会沿着协议栈向上完整地处理收取的广播数据报,直到该数据报在UDP层处理时才被丢弃。


在这里插入图片描述

🎄二、广播地址

广播的实现需要往广播地址发送广播分组。任何子网中最后一个IP地址就是广播地址。如果以{子网ID,主机ID}表示一个IP地址,则广播地址分为下面两种,-1表示所有位都位1:

  • 1、子网定向广播地址:{子网ID,-1}。这是子网上所有接口的广播地址。举例来说,假设有一个 192.168.1/24 的子网,那么这个子网中最后一个IP就是 192.168.1.255/24 ,它就是该子网所有接口的子网定向广播地址。通常,路由器不转发目的地址为子网定向广播地址的数据报。
  • 2、受限广播地址:{子网ID,-1} 或 255.255.255.255。路由器从不转发目的地址为255.255.255.255的IP数据报。当应用程序设置了SO_BROADCAST套接字选项,且发送目的地址为255.255.255.255的UDP数据报时,大多数主机会将该目的地址转换成外出接口的子网定向广播地址并发送到路由器。
    255.255.255.255用十六进制表示就是0xffffffff。在Linux系统中,定义了一个宏INADDR_BROADCAST来表示受限广播地址,该宏定义在头文件<netinet/in.h>中,定义如下:
    /* Address to send to all hosts.  */
    #define INADDR_BROADCAST        ((in_addr_t) 0xffffffff)
    

在这里插入图片描述

🎄三、UDP单播 和 UDP广播 的比较

✨3.1 UDP单播过程

下图说明了一个UDP数据报在单播情况下,怎样到达目的地的:
在这里插入图片描述

  • 发送主机(图中最左边):

    • ①进程在一个UDP套接字上调用sendto往IP地址192.168.42.3端口7433发送一个数据报;
    • ②UDP层对它冠以一个UDP首部后把UDP数据报传递到IP层
    • ③IP层对它冠以一个IPv4首部,确定其外出接口,在以太网情况下还激活ARP把目的IP地址映射成相应的以太网地址:
      00:0a:95:79:bc:b4。
    • ④该分组然后作为一个目的以太网地址为这个48位地址的以太网帧发送出去。该以太网帧的帧类型字段值为表示IPv4分组的0x0800
  • 目的主机(图中最右边):

    • ①目的主机的以太网接口看到该帧后,比较并确定该帧的目的地址和自己以太网地址相同,于是读入该帧。由于帧类型是0x0800,该帧的分组被置于IP输入队列。
    • ②IP层处理该分组会先比较该分组的目的地址,确定是本机IP地址之一才接受该分组,接着查看IPv4首部的协议字段,值为表示UDP的17,于是将该分组承载的数据报传递到UDP层。
    • ③UDP层检查该数据报的目的端口,把该数据报置于相应套接字的接收队列,必要时会唤醒阻塞在该相应输入操作的进程,由进程读取这个新收取的数据报。
  • 其他主机(图中中间):

    • ①非目的主机的以太网口看到该帧后,比较该帧目的地址和自己以太网地址,比较结果不相同,于是忽略了这个帧。

✨3.2 UDP广播过程

下图说明了一个UDP数据报在广播情况下,怎样到达目的地的:
在这里插入图片描述

  • 发送主机(图中最左边):

    • ①进程在一个UDP套接字上调用sendto往IP地址192.168.42.255端口520发送一个数据报;
    • ②UDP层对它冠以一个UDP首部后把UDP数据报传递到IP层;
    • ③IP层对它冠以一个IPv4首部,确定其外出接口,因为目的地址是子网定向广播地址,所以映射为48位全为1的以太网地址:ff:ff:ff:ff:ff:ff
    • ④该分组作为一个目的以太网地址为ff:ff:ff:ff:ff:ff的以太网帧发送出去,使得该子网的每个以太网接口(包括自身主机接口)都会接收到该帧。该以太网帧的帧类型字段值为表示IPv4分组的0x0800
  • 子网内所有主机(包括自身主机):

    • ①子网内的所有主机的以太网接口看到该帧后,因为目的地址是ff:ff:ff:ff:ff:ff,都会接收该帧。由于帧类型是0x0800,该帧的分组传递到IP层。
    • ②IP层确定该分组目的地址为广播地址后,会接受该分组,接着查看IPv4首部的协议字段,值为表示UDP的17,于是将该分组承载的数据报传递到UDP层。
    • ③UDP层检查该数据报的目的端口,如果接收到该数据报的主机没有任何进程绑定值为520的UDP端口,则该主机的UDP代码会丢弃已收取的数据报,如图中中间主机。如果接收到该数据报的主机存在进程绑定了值为520的UDP端口,那么该进程把该数据报置于相应套接字的接收队列,必要时会唤醒阻塞在该相应输入操作的进程,由进程读取这个新收取的数据报。

在这里插入图片描述

🎄四、UDP实现广播的例子

下面给出一个使用UDP实现广播的例子,代码是之前文章的例子 入门知识:UDP协议、一个最简单的UDP客户端、一个最简单的UDP服务端 。

只需要在原本客户端修改两个地方就可以发送UDP广播数据报了:一个是在sendto之前设置套接字选项SO_BROADCAST;另一个是将sendto的目的地址设置为广播地址,这里使用INADDR_BROADCAST

发送UDP广播客户端步骤:

  • 1、创建UDP套接字socket;
  • 2、准备广播地址和端口;
  • 3、设置广播套接字选项 SO_BROADCAST;
  • 4、使用 sendto 发送广播数据报;
  • 5、处理应答
  • 6、关闭

UDP广播客户端代码也很简单,如下:

// brocastCli.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

#define USE_BRORDCAST 1

int main()
{
	// 1、创建UDP套接字socket
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd<0)
		perror("socket error" );
	
	// 2、准备广播地址和端口
	struct sockaddr_in servaddr;
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons (10086);
#if USE_BRORDCAST
	servaddr.sin_addr.s_addr = INADDR_BROADCAST;
	
	// 3、设置广播套接字选项 SO_BROADCAST
	int so_broadcast = 1;
	if(setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&so_broadcast,sizeof(so_broadcast)) < 0)
	{
		perror("setsockopt");
		close(sockfd);
		return -1;
	}
#else
	if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0) // 设置本机IP为服务端IP
		perror("inet_pton error");
#endif
		
	// 4、使用 sendto 发送广播数据报
	if(sendto(sockfd, "Hello,I am udp client", strlen("Hello,I am udp client"), 0, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		perror("sendto error" );
	
	// 5、处理应答
	char recvline[256];
	int n = 0;
	struct sockaddr_in tmpAddr;
	bzero(&tmpAddr, sizeof(tmpAddr));
	socklen_t addrLen=sizeof(tmpAddr);
	while ( (n = recvfrom (sockfd, recvline, sizeof(recvline), 0, (struct sockaddr*)&tmpAddr, &addrLen)) > 0)
	{
		recvline[n] = 0 ;/*null terminate */
		printf("recvfrom ip=[%s], [%s]\n",inet_ntoa(tmpAddr.sin_addr), recvline);
		bzero(&tmpAddr, sizeof(tmpAddr));
	}
	
	if (n < 0)
		perror("read error" );
	
	// 6、关闭
	close(sockfd);

	return 0;
}

UDP服务器不需要做任何改动,还是使用之前的例子,代码如下:

// brocastSer.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main()
{
	// 1、创建UDP套接字socket
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd<0)
		perror("socket error" );
	
	// 2、准备服务端ip和端口
	struct sockaddr_in servaddr;
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons (10086);
	servaddr.sin_addr.s_addr = INADDR_ANY; // 指定ip地址为 INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任一网络接口上接受客户端的连接
	
	// 3、绑定 bind
	if (bind(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		perror("bind error" );
	
	// 4、使用 sendto、recvfrom 交互数据
	
	printf("UdpSer sockfd=%d, start \n",sockfd);
	char recvline[256];
	while(1)
	{
		struct sockaddr_in cliaddr;
		bzero(&cliaddr, sizeof(cliaddr));
		socklen_t addrLen=sizeof(cliaddr);
		int n = recvfrom(sockfd, recvline, sizeof(recvline), 0, (struct sockaddr*)&cliaddr, &addrLen);
		if(n>0)
		{
			recvline[n] = 0 ;/*null terminate */
			printf("recv sockfd=%d %d byte, [%s] addrLen=%d, cliIp=%s, cliPort=%d\n",
				sockfd, n, recvline, addrLen, inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
			sendto(sockfd, "Hello,I am udp server", strlen("Hello,I am udp server"), 0, (struct sockaddr*)&cliaddr, addrLen);
		}
	}
	
	// 5、关闭
	close(sockfd);

	return 0;
}

分别在局域网内的几台机器运行UDP服务端brocastSer,然后在其中一台机器运行广播客户端brocastCli,下面是客户端的运行结果,收取到好几台运行着服务端主机对广播的响应:
在这里插入图片描述

在这里插入图片描述

🎄五、总结

👉本文介绍了广播的概念、广播的用途、广播的缺点、广播地址,对比了单播和广播的流程,最后给出了UDP实现广播的C语言例子。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
《Unix网络编程卷1》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于出国留学和考研比较----以本人双非跨考计算机为例

文章目录 中心论点国内就业现状勿让旧认知害了自己那出国留学真的一无是处了吗?1. 藤校仍旧是具有极高价值2. 时间成本低3. 研究生一定比单纯的本科找工作强!4. 很多人说出国读博好,可以无脑入,真是这样吗? 中心论点 如果在选择出国留学还是国内考研的最终核心诉求都是有更好…

LSTM长短期记忆网

笔记来源—— 【重温经典】大白话讲解LSTM长短期记忆网络 如何缓解梯度消失&#xff0c;手把手公式推导反向传播 LSTM网络结构 RNN结构 下面拉出一个单元结构进行讲解 &#xff1a;记忆细胞&#xff0c;t-1时刻的记忆细胞 :表示状态,t-1时刻的状态 正是这样经过了一个单元&a…

Oracle.xs.dll‘ for module DBD::Oracle: load_file:找不到指定的模块

安装Ora2pg时,碰到 异常现象 D:\ProgramFiles\ora2pg>ora2pg -t show_report --estimate_cost -c ora2pg_conf.dist install_driver(Oracle) failed: Cant load D:/ProgramFiles/strawberry/perl/site/lib/auto/DBD/Oracle/Oracle.xs.dll for module DBD::Oracle: load_fil…

Nginx使用—基础知识

Nginx简介 Nginx优点 高性能、高并发 支持很高的并发&#xff0c;在处理大量并发的情况下&#xff0c;比其他web服务器要高效 轻量且高扩展 功能模块少(源代码仅保留http与核心模块代码&#xff0c;其余不够核心代码会作为插件来安装) 代码模块化&#xff08;易读&#xff0…

win10虚拟机安装驱动教程

在虚拟机菜单栏中选择安装VMware Tools&#xff1a; 安装好后&#xff0c;在虚拟机中打开此电脑&#xff0c;双击DVD驱动器进行安装&#xff1a; 一直点击下一步&#xff1a; 安装完成&#xff1a; 此时重启虚拟机&#xff0c;发面小屏幕页面的虚拟机自动占满了全部屏幕&#x…

Docker常用基础指令

目录 1 前言 2 常用指令 2.1 获取帮助 2.2 拉取镜像到本地 2.3 对本地镜像进行打包 2.4 对本地镜像的删除 2.5 通过tar包加载本地镜像 2.6 查看所有镜像 2.7 创建新的容器 2.8 查看容器 2.9 停止容器运行 2.10 运行容器 2.11 删除容器 2.12 查看容器日志 2.13 进…

ImportError: Could not import docarray python package解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

springcloud和基础服务的搭建以及封装

代码仓库地址&#xff1a;https://github.com/zhaoyiwen-wuxian/springcloud-common page分页也进行了封装&#xff0c;只需要添加到pom中&#xff0c;将会自动进行分页&#xff0c;并且后端不需要写任何的分页数据。只需要前端自己传分页参数即可&#xff0c;并且里面封装了很…

Clickhouse: 数据基本知识

产品概述 ClickHouse是一个开源的列式数据库管理系统&#xff0c;专门用于在线分析处理&#xff08;OLAP&#xff09;场景。它具有高性能、高可靠性、高可扩展性和低成本等优点&#xff0c;被广泛应用于大数据领域。 以下是ClickHouse的主要特点&#xff1a; 高性能&#xff…

挑战杯 基于深度学习的人脸表情识别

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸表情识别 该项目较…

Unity角色动画变成半跪\半蹲\下沉 的问题

导入的人物动画发生如图形态 解决方法&#xff1a;找到动画模型&#xff0c;Rig - AnimationType 改为Humanoid &#xff0c;然后Apply一下

Vue+OpenLayers7入门到实战目录

前言 本篇作为《VueOpenLayers7入门到实战》所有文章的二合一汇总目录&#xff0c;方便查找。 本专栏源码是由OpenLayers7.x版本结合Vue框架编写。 本专栏从Vue搭建脚手架到如何引入OpenLayers7依赖的每一步详细新手教程&#xff0c;再到通过各种入门案例和综合性的实战案例&a…

【MySQL】视图、索引

目录 视图视图的用途优点视图的缺点创建视图查看视图修改视图删除视图注意事项 索引索引的原理索引的数据结构二分查找法Hash结构Hash冲突&#xff01;&#xff01;&#xff01; B树二叉查找树 存在问题改造二叉树——B树降低树的高度 B树特点案例继续优化的方向 改造B树——B树…

项目管理工具及模板总结|甘特图|OKR周报|任务管理|头脑风暴

项目管理常用模板大全&#xff1a; 1. 项目组OKR周报 2. 项目组传统周报工作法 3. 项目甘特图 4. 团队名单 5. 招聘跟进表 6. 出勤统计 7. 年度工作日历 8. 项目工作年计划 9. 版本排期 10. 项目组任务管理 11. 项目规划模板 12. 产品分析报告 13. 头脑风暴 软件开发管理全套标…

Linux CentOS安装教程

CentOS是一种基于Linux的免费、开源的操作系统&#xff0c;它是Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码的重建版本。CentOS致力于提供稳定、可靠的服务器操作系统&#xff0c;广泛应用于企业级服务器和Web服务器。它具有强大的安全性和稳定性&#xff0c;…

阿里云服务器配置CPU、内存、存储、带宽选择方法

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

Vue+腾讯地图-实现关键词输入提示功能

不废话&#xff0c;上代码~~~ 效果图&#xff1a; 1、先去腾讯地图后台创建自己的应用获取到应用的 Key 腾讯地图后台地址&#xff1a;腾讯位置服务 - 立足生态&#xff0c;连接未来 创建应用的 Key 如下&#xff1a; 2、在项目中添加腾讯地图API的js插件&#xff0c;如…

Thinkphp5.1中,将数组赋值给js使用

一、例如Thinkphp5.1中的的代码是这样的 $data [status > 1,msg > 加载成功,data > [id > 1,username > 小洪帽,] ];$this->assign(data,$data);二、JS代码接收PHP中的数组 注意 <> 符号是不需要放引号的。 let arr <?json_encode($data)?>…

LeetCode 刷题 [C++] 第98题.验证二叉搜索树

题目描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 题目分析 由题…

【排序】详解冒泡排序

一、思想 冒泡排序的基本思想是利用两两比较相邻记录的方式&#xff0c;通过一系列的比较和交换操作&#xff0c;使得较大或较小的元素逐渐移动到数列的一端。在每一轮的排序过程中&#xff0c;都会从数列的起始位置开始&#xff0c;对相邻的元素进行比较&#xff0c;如果它们…