网络编程(UDP编程)

news2025/6/12 18:10:10

思维导图

UDP基础编程(单播)

1.流程图

服务器:短信的接收方

  1. 创建套接字 (socket)-----------------------------------------》有手机
  2. 指定网络信息-----------------------------------------------》有号码
  3. 绑定套接字 (bind)------------------------------------------》插卡
  4. 接收发送消息(recvfrom sendto)---------------------》收短信
  5. 关闭套接字(close)------------------------------------------》接收完毕

客户端:短信的发送方

  1. 创建套接字 (socket)------------------------------------------》有手机
  2. 指定网络(服务器)信息---------------------------------》有对方号码
  3. 接收发送消息(recvfrom sendto)----------------------》发短信
  4. 关闭套接字(close)----------------------------------------》发送完毕

2.函数接口

2.1.recvfrom

recvfrom的最后连两个参数与accept最后两个参数起到了相同的作用,就可以获取到发消息的是谁或者说是接收到的是谁的消息

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

头文件:#include <sys/types.h>
              #include <sys/socket.h>

功能:接收数据

参数:sockfd:套接字描述符

           buf:接收缓存区的首地址

           len:接收缓存区的大小

           flags:0

           src_addr:发送端的网络信息结构体的指针

           addrlen:发送端的网络信息结构体的大小的指针

返回值:成功:接收的字节个数

              失败:-1

              客户端退出:0

2.2.sendto

sendto的最后连两个参数与connect最后两个参数传参一样,要确定将消息发送给谁,或者说是接收者信息(一般是服务器)。

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

头文件:#include <sys/types.h>
              #include <sys/socket.h>

功能:发送数据

参数:sockfd:套接字描述符

           buf:发送缓存区的首地址

           len:发送缓存区的大小

           flags:0

           src_addr:接收端的网络信息结构体的指针

           addrlen:接收端的网络信息结构体的大小

返回值: 成功:发送的字节个数

               失败:-1

3.代码实现:

服务器:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	int rd = 0;
	char buf[32] = "";
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);		//创建套接字
	if(sockfd < 0){
		perror("socket err");
		return -1;
	}
	
	struct sockaddr_in server_addr,clientaddr;		//服务器配置
	server_addr.sin_family = AF_INET;				//地址族选择ipv4
	server_addr.sin_port = htons(atoi(argv[1]));	//通过终端命令行参数获取端口号(终端输入的都是字符类型,要把其转换为int类型)
    server_addr.sin_addr.s_addr = INADDR_ANY;                   //C语言内自带的宏定义,代表0.0.0.0地址
	int len = sizeof(clientaddr);

	//绑定套接字
	if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0){
		perror("bind err");
		close(sockfd);
		return -1;
	}
	
	//循环实现:当客户端退出连接,会继续等待连接新的客户端来连接服务器通信
	while(1){
		rd = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&clientaddr,&len);
		if(rd < 0){
			perror("recvfrom err");
			continue;
		}
		else
			printf("ip:%s port:%d buf:%s\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),buf);
		memset(buf,0,sizeof(buf));
	}
	close(sockfd);				//关闭socket文件描述符号
	return 0;
}

 客户端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char buf[32] = "";
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);		//创建套接字
	if(sockfd < 0){
		perror("socket err");
		return -1;
	}

	struct sockaddr_in server_addr;					//服务器配置
	server_addr.sin_family = AF_INET;				//地址族选择ipv4
	server_addr.sin_port = htons(atoi(argv[2]));					//配置连接目标的服务器的端口号
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);				//通过终端命令行参数获取ip地址作为服务器地址
	
	//循环输入数据
	while(1){
		fgets(buf,sizeof(buf),stdin);				//从终端输入流获取字符串更安全,超出字符串长度不会段错误
		if(buf[strlen(buf)-1] = '\n')				//去除字符串尾部添加的\n
			buf[strlen(buf)-1] = '\0';
		if( strcmp(buf,"quit") == 0 )				//当输入quit,退出客户端
			break;
		sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
		memset(buf,0,sizeof(buf));
	}
	close(sockfd);
	return 0;
}

4.TCP与UDP区别:

特性TCP (传输控制协议)UDP (用户数据报协议)
连接性面向连接 (需先建立连接)无连接 (直接发送)
可靠性高可靠 (确认、重传、排序)不可靠 (不保证送达、顺序、不重传)
数据传输

字节流 (无明确边界,保证顺序)

使用流式套接字(SOCK_STREAM)

数据报文 (有明确边界,可能乱序、丢失)

使用数据报套接字(SOCK_DGRAM)

速度相对较慢 (建立连接、确认等开销)非常快 (几乎无额外开销)
开销较大 (头部20-60字节,维护连接状态)极小 (固定8字节头部)
拥塞控制 (复杂算法动态调整发送速率) (应用需自行处理)
流量控制 (接收方窗口控制发送方速率)
应用场景要求可靠、完整传输:Web(HTTP)、邮件(SMTP/POP/IMAP)、文件传输(FTP)、远程登录(SSH)要求实时性、速度:音视频流、在线游戏、DNS查询、广播、实时监控

 广播

1.基础复习

广播地址:

1.受限广播地址:255.255.255.255。数据包会被发送到发送主机所在的整个本地网络段的所有主机。路由器通常不会转发这种广播包(限制在本地网段)。

2.定向广播地址: 形网络前缀.255(对于传统的/24 子网)或网络前缀.全1主机部分 (对于其他子网掩码)。例如,在192.168.1.0/24网段中,定向广播地址是192.168.1.255。这种广播理论上可以被路由器转发到目标网络,但出于安全考虑,现代路由器默认通常也会阻止转发此类广播。(主机号最大的地址是该网段的广播地址 如:192.168.50.222的广播地址是192.168.50.255)

2.函数接口:

int setsockopt(int sockfd,int level,int optname,void *optval,socklen_t optlen)

头文件:#include <sys/types.h>
               #include <sys/socket.h>

功能:获得/设置套接字属性

参数:sockfd:套接字描述符

           level:协议层

           optname:选项名

           optval:选项值

           optlen:选项值大小

返回值: 成功 0 失败-1

3.分析:

发送广播:将socket转换后,改变要发送的目标ip地址为当前网络的广播就可以

接收广播: 接收方不需要特殊设置(不像多播需要加入组),只需绑定到 INADDR_ANY (0.0.0.0) 和发送方使用的端口号,并在该端口上监听 UDP 数据报即可接收到广播消息。

接收者(服务器)

        1.创建套接字(socket)

        2.指定网络信息

        3.绑定套接字(bind)

        4.接收消息(recvfrom)

        5.关闭套接字(close)

发送者

        1.创建套接字(socket)

        2.由于原本的套接字不支持广播,所以要给套接字设置广播属性

                    int optval = 1;
                    setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeof(optval));   

        3.指定网络(服务器)信息

        4.发送消息(sendto)

        5.关闭套接字(close)

4.代码:

客户端(服务器端不用改):

​
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char buf[32] = "";
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);		//创建套接字
	if(sockfd < 0){
		perror("socket err");
		return -1;
	}
	int optval = 1;
	setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeof(optval));    //改变套接字为广播

	struct sockaddr_in server_addr;					//服务器配置
	server_addr.sin_family = AF_INET;				//地址族选择ipv4
	server_addr.sin_port = htons(atoi(argv[2]));					//配置连接目标的服务器的端口号
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);				//通过终端命令行参数获取ip地址作为服务器地址(广播地址)
	
	//循环输入数据
	while(1){
		fgets(buf,sizeof(buf),stdin);				//从终端输入流获取字符串更安全,超出字符串长度不会段错误
		if(buf[strlen(buf)-1] = '\n')				//去除字符串尾部添加的\n
			buf[strlen(buf)-1] = '\0';
		if( strcmp(buf,"quit") == 0 )				//当输入quit,退出客户端
			break;
		sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
		memset(buf,0,sizeof(buf));
	}
	close(sockfd);
	return 0;
}

5.缺点:

广播方式会把信息发给连接当前网络的所有的主机,过多的广播会大量的占用网络带宽,造成广播风暴,影响正常的通信

广播风暴: 网络长时间被大量的广播数据包所占用,使正常的点对点通信无法正常进行,其外在表现为网络速度奇慢无比,甚至导致网络瘫痪

组播(多播)

1.什么是组播

1.多播是一个人发送,加入到多播组的人接收数据。

2.多播方式既可以发给多个主机,又能避免像广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

    2.多播地址

    D类:224.0.0.1-239.255.255.254

    3.流程

    接收者(服务器)

            1.创建套接字(socket)

            2.设置多播属性,将自己的IP加入到多播组中

                    struct ip_mreq mreq;

                    mreq.imr_multiaddr.s_addr = inet_addr(argv[2]); // 组播IP

                    mreq.imr_interface.s_addr = INADDR_ANY; // 自己IP

                    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq));

            3.指定网络信息

            4.绑定套接字(bind)

            5.接收消息(recvfrom)

            6.关闭套接字(close)

    发送者

            1.创建套接字(socket) 

            2.指定网络(服务器)信息

            3.发送消息(sendto)

            4.关闭套接字(close)

    4.代码实现:

    服务器端:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <stdlib.h>
    
    int main(int argc, const char *argv[])
    {
    	int rd = 0;
    	char buf[32] = "";
    	int sockfd = socket(AF_INET,SOCK_DGRAM,0);		//创建套接字
    	if(sockfd < 0){
    		perror("socket err");
    		return -1;
    	}
    	struct ip_mreq mreq;
    	mreq.imr_multiaddr.s_addr = inet_addr(argv[2]);         // 组播IP
    	mreq.imr_interface.s_addr = INADDR_ANY;                 // 自己IP
    	setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
    
    	struct sockaddr_in server_addr,clientaddr;		        //服务器配置
    	server_addr.sin_family = AF_INET;				        //地址族选择ipv4
    	server_addr.sin_port = htons(atoi(argv[1]));	        //通过终端命令行参数获取端口号(终端输入的都是字符类型,要把其转换为int类型)
        server_addr.sin_addr.s_addr = INADDR_ANY;                   //C语言内自带的宏定义,代表0.0.0.0地址
    	int len = sizeof(clientaddr);
    
    	//绑定套接字
    	if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0){
    		perror("bind err");
    		close(sockfd);
    		return -1;
    	}
    	
    	//循环实现:当客户端退出连接,会继续等待连接新的客户端来连接服务器通信
    	while(1){
    		rd = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&clientaddr,&len);
    		if(rd < 0){
    			perror("recvfrom err");
    			continue;
    		}
    		else
    			printf("ip:%s port:%d buf:%s\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),buf);
    		memset(buf,0,sizeof(buf));
    	}
    	close(sockfd);				//关闭socket文件描述符号
    	return 0;
    }

    实现效果:

    UDP广播三种实现形式比较

    特性单播 (Unicast)组播 (Multicast)广播 (Broadcast)
    通信模式一对一 (1:1)一对多 (1:N)一对所有 (1:All)
    目标地址单个 特定主机地址 (192.168.1.100)组播组地址 (Class D, 224.0.0.1~239.255.255.254)广播地址 (255.255.255.255 或 192.168.1.255)
    接收者唯一 指定的目标主机加入该组播组 的所有主机同一网络段 的所有主机
    效率低效 (N份数据,N次传输)高效 (1份数据,网络复制)低效 (1份数据,所有主机处理)
    网络范围可跨越 任意网络 (路由可达即可)可跨越 多个网络 (需路由器支持 IGMP/PIM)通常限制在 单个本地网络段 (LAN)
    协议支持TCP & UDP (主要方式)UDP (主要方式,TCP 不原生支持)UDP (TCP 不支持)
    资源消耗发送端数量与网络带宽消耗:随接收者数量线性增长发送端与骨干网络带宽消耗:恒定 (与接收者数无关)发送端带宽消耗:一次,但所有主机都会接收处理
    应用场景Web浏览 (HTTP)、文件传输 (FTP/SCP)、邮件、SSH、数据库访问视频会议、IPTV直播、股票行情、软件分发、网络发现DHCP、ARP、局域网简单服务发现、旧式网络游戏
    路由器处理正常路由 (基于目标IP路由表)特殊路由 (需支持 IGMP/PIM,复制数据到下游有接收者的网段)通常不转发 (限制在本地网段)
    实现关键目标主机IP地址setsockopt(IP_ADD_MEMBERSHIP) 加入组播组setsockopt(SO_BROADCAST) 启用广播权限
    优点可靠(TCP)、点对点控制、简单高效节省带宽、支持大规模接收者、跨网段实现简单、本地网段全覆盖
    缺点接收者多时效率低、消耗大量发送端和网络资源实现较复杂、需网络设备支持、可靠性需应用层保障浪费资源(非目标主机也处理)、安全隐患、无法跨路由

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

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

    相关文章

    Java面试专项一-准备篇

    一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…

    全志A40i android7.1 调试信息打印串口由uart0改为uart3

    一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…

    pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

    目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…

    Redis数据倾斜问题解决

    Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…

    mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

    文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…

    Map相关知识

    数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…

    selenium学习实战【Python爬虫】

    selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…

    图表类系列各种样式PPT模版分享

    图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…

    tree 树组件大数据卡顿问题优化

    问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…

    Spring数据访问模块设计

    前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…

    如何在最短时间内提升打ctf(web)的水平?

    刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…

    如何理解 IP 数据报中的 TTL?

    目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…

    Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

    参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…

    C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

    1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…

    【Oracle】分区表

    个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…

    【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

    目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…

    RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

    本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…

    学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

    每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…

    3-11单元格区域边界定位(End属性)学习笔记

    返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…

    蓝桥杯3498 01串的熵

    问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…