网络编程 | UDP套接字通信及编程实现经验教程

1、UDP基础

        传输层主要应用的协议模型有两种,一种是TCP协议,另外一种则是UDP协议。在上一篇博客文章中,已经对TCP协议及如何编程实现进行了详细的梳理讲解,在本文中,主要讲解与TCP一样广泛使用了另一种协议:UDP(User Datagram Protocol)用户数据报协议。

        网络编程 | TCP套接字通信及编程实现经验教程_tcp套接字通讯-CSDN博客

        TCP协议在网络通信中占主导地位,绝大多数的网络通信借助TCP协议完成数据传输。但UDP也是网络通信中不可或缺的重要通信手段。相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠的报文传输协议”。

        每个 UDP 报文分为 UDP 报头和 UDP 数据区两部分。其中,报头由 4 个 16 位长(2 字节)字段组成,分别说明该报文的源端口、目的端口、报文长度和校验值。

①、源端口:16位长(2字节),标识发送该报文的进程端口号。如果不需要回复,则可以设置为0。

②、目的端口:同样是16位长(2字节),标识接收该报文的目标进程端口号。

③、报文长度:16位长(2字节),表示整个UDP报文(包括报头和数据区)的长度,单位是字节。最小值为8字节,即只有UDP报头而没有数据的情况。

④、校验值:16位长(2字节),用于错误检查。它覆盖了UDP报头和数据以及一个伪头部的信息。计算时还包括IP头部的一些字段来确保数据在传输过程中没有被修改。如果校验和未计算(通常是为了提高效率),则此字段设置为0。

UDP的特点:

①、无连接:与TCP不同,UDP在传输数据前不需要建立连接,减少了通信的准备时间。

②、不可靠传输:没有像TCP那样的确认机制、重传机制以及流量控制,因此无法保证数据能够准确无误地到达接收端。

③、面向数据报:每个UDP报文独立处理,大小限制通常为64KB以内,包括头部信息。

④、全双工操作:允许同时进行双向的数据传输。

⑤、高效性:由于省去了连接建立的过程,UDP具有较低的延迟和较高的传输效率。

UDP的应用场景:

①、实时性要求高的应用:例如视频会议、电话会议等需要快速传递信息的服务,即使偶尔丢失一些数据包也不会严重影响整体体验。

②、对少量数据丢失容忍度高的应用:比如在线游戏、VoIP等,这类应用更注重数据传输的速度而非完整性。

③、广播/组播通信:UDP支持一对多的通信模式,适合用于广播或组播式的信息发布。

④、即时通讯软件:许多即时通讯工具使用UDP来实现文本消息及音视频通话功能,因为它可以提供更快的响应速度。

解决UDP丢包问题的方法:

①、服务器端设计流量控制:通过调整发送速率避免因过快发送数据而导致的丢包现象。

②、调整接收缓冲区大小:利用setsockopt函数动态修改接收缓冲区容量,以适应不同的网络状况。

        为了便于读者更加直观的理解UDP,并了解UDP和TCP之间的最明显的区别,绘制了如下所示的简易图。

2、UDP通信流程

        相比于TCP,由于UDP是无连接的协议,任何一方都可以在任意时刻发送数据报给另一方,服务器和客户端的界限并没有那么清晰,且客户端与服务器的通信代码流程几乎相同,也就没有非常严格的客户端与服务器之分。但为了保持程序代码框架的逻辑清晰以及便于理解和维护,因此还是需要指定一端作为服务器、另一端作为客户端。

        由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,保证通讯可靠性的机制需要在上层应用层中根据需要,通过优化程序的逻辑架构来完善。

3、UDP通信代码

3.1、UDP常用API接口

(1)、socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);函数功能:用于创建一个新的套接字,它支持多种协议和地址族,可以用来实现不同类型的网络通信。
参数说明:domain: 指定通信域,常用的有:AF_INET:IPv4互联网协议族。AF_INET6:IPv6互联网协议族。type: 指定套接字类型,对于UDP应使用:SOCK_DGRAM:支持UDP协议的数据报套接字。protocol: 指定使用的协议。对于UDP来说,虽然可以指定为IPPROTO_UDP,但更常见的是设置为0,表示使用由type参数决定的默认协议(在这种情况下就是UDP)。
返回值:成功时,返回一个非负整数,代表新创建的套接字描述符。失败时,返回-1,并且会设置全局变量errno来指示具体的错误原因。
(2)、bind
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);函数功能:为套接字sockfd分配一个本地地址addr。如果要接收发送到特定端口的数据报时,必须先调用此函数。
参数说明:sockfd:由socket()函数创建的套接字描述符。addr:指向sockaddr结构的指针,该结构包含欲绑定到套接字的地址信息。对于IPv4,通常使用sockaddr_in结构,并需要将其转换为sockaddr*类型来传递给bind()。addrlen:addr参数所指向的数据结构的大小,以字节为单位。对于IPv4的sockaddr_in结构体,通常是sizeof(struct sockaddr_in)。
返回值:成功时,返回0。失败时,返回-1,并设置errno变量指示错误原因。
(3)、sendto
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);函数功能用于从套接字 sockfd 发送数据。当使用 UDP 协议时,这个函数可以将数据报发送到由 dest_addr 指定的目的地。
参数说明sockfd: 已经创建好的UDP套接字描述符。buf: 指向要发送的数据缓冲区的指针。len: 缓冲区 buf 中数据的长度(以字节为单位)。flags: 调用操作选项,通常设置为0。dest_addr: 目标地址,指向一个包含目标IP地址和端口号的 sockaddr 结构体。对于IPv4,这通常是 sockaddr_in 类型的结构体。addrlen: dest_addr参数所指向的数据结构的大小,以字节为单位。
返回值成功时,返回实际发送的字节数。失败时,返回 -1,并设置 errno 变量指示错误原因。
(4)、recvfrom
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);函数功能用于从套接字 sockfd 接收数据。此函数可以接收来自任意发送方的UDP数据报,并可选性的保存发送方的地址信息。
参数说明sockfd: 已经创建并绑定到本地地址的UDP套接字描述符。buf: 指向缓冲区的指针,用于存储接收到的数据。len: 缓冲区 buf 的大小(以字节为单位)。flags: 调用操作选项,通常设置为0。src_addr: 指向一个 sockaddr 结构体的指针,该结构体将保存发送方的地址信息。如果不需要获取发送方地址,此参数可以为NULL。addrlen: 指向 socklen_t 类型变量的指针,表示 src_addr 结构体的大小。调用前应初始化为该结构体的大小;返回时包含实际存储在 src_addr 中的地址信息长度。
返回值成功时,返回实际接收到的字节数。失败时,返回 -1,并设置 errno 变量指示错误原因。
(5)、close
#include <unistd.h>
int close(int fd);函数功能关闭由文件描述符 fd 指定的文件或套接字,并释放所有与之相关的资源。
函数参数fd: 要关闭的文件描述符或套接字描述符。
返回值成功时返回 0。失败时返回 -1,并设置 errno 变量来指示错误类型。

3.2、UDP参考程序

(1)UDP 客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define PORT 8080
#define SERVER_IP "127.0.0.1"int main(int argc, char **argv)
{int sock_client;struct sockaddr_in servaddr, cliaddr;char message[1024];char buffer[1024];socklen_t addrLen = sizeof(cliaddr);// 创建UDP套接字if ((sock_client = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}printf("UDP client socket create success!\n");memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);while(1) {printf("Input data to send to server: ");fgets(message, 1024, stdin);// 发送数据到服务器sendto(sock_client, (const char *)message, strlen(message), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));// 接收来自服务器的数据ssize_t n = recvfrom(sock_client, (char *)buffer, 1024, MSG_WAITALL, (struct sockaddr *) &cliaddr, &addrLen);buffer[n] = '\0';// 打印接收到的消息、服务器IP地址和端口号printf("Received from udp server: %s", buffer);printf("Server IP: %s\tPort: %d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));}close(sock_client);return 0;
}
(2)UDP服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 8080int main(int argc, char **argv) 
{int sock_server;char buffer[1024];struct sockaddr_in servaddr, cliaddr;socklen_t len;// 创建UDP套接字if ((sock_server = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}printf("UDP server socket create success!\n");memset(&servaddr, 0, sizeof(servaddr));memset(&cliaddr, 0, sizeof(cliaddr));servaddr.sin_family = AF_INET; // IPv4servaddr.sin_addr.s_addr = INADDR_ANY;servaddr.sin_port = htons(PORT);// 绑定套接字到指定端口if (bind(sock_server, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {perror("bind failed");close(sock_server);exit(EXIT_FAILURE);}printf("Server is listening on port %d...\n", PORT);while(1) {len = sizeof(cliaddr);// 接收来自客户端的数据ssize_t n = recvfrom(sock_server, (char *)buffer, 1024, MSG_WAITALL, (struct sockaddr *) &cliaddr, &len);buffer[n] = '\0';// 打印接收到的消息、客户端IP地址和端口号printf("Received from UDP client: %s", buffer);printf("Client IP: %s\tPort: %d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));// 发送数据回客户端sendto(sock_server, (const char *)buffer, strlen(buffer), MSG_CONFIRM, (const struct sockaddr *) &cliaddr, len);}close(sock_server);return 0;
}

        上述代码的验证结果如下如下所示。

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

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

相关文章

大象机器人发布首款穿戴式数据采集器myController S570,助力具身智能数据收集!

myController S570 具有较高的数据采集速度和远程控制能力&#xff0c;大大简化了人形机器人的编程。 myController S570 是一款可移动的轻量级外骨骼&#xff0c;具有 14 个关节、2 个操纵杆和 2 个按钮&#xff0c;它提供高数据采集速度&#xff0c;出色的兼容性&#xff0c…

数据库存储上下标符号,sqlserver 2008r2,dm8

sqlserver 2008r2&#xff1a; 数据类型需要用nvarchar插入数据时字符串前需要用N create table test( col1 varchar(50), col2 nvarchar(50) ) insert into test(col1,col2) values(U⁴⁵⁶⁷⁸⁹⁰D₁₂₃₄₅₆₇₈₉₀,U⁴⁵⁶⁷⁸⁹⁰D₁₂₃₄₅₆₇₈₉₀) insert into…

海康工业相机的应用部署不是简简单单!?

作者&#xff1a;SkyXZ CSDN&#xff1a;SkyXZ&#xff5e;-CSDN博客 博客园&#xff1a;SkyXZ - 博客园 笔者使用的设备及环境&#xff1a;WSL2-Ubuntu22.04MV-CS016-10UC 不会吧&#xff1f;不会吧&#xff1f;不会还有人拿到海康工业相机还是一脸懵叭&#xff1f;不会还有人…

为什么要内存对齐?

内存对齐 初步认识 内存对齐是什么&#xff1f;从下面的代码可以比较直观地有一个简单的认识&#xff1a; #include <iostream>using namespace std;struct s1 {int i;char c1;char c2; };struct s2 {char c1;int i;char c2; };struct s3 {char c1;char c2;int i; };i…

【SQL 中的分组查询与联合查询详解】

文章目录 SQL 中的分组查询与联合查询详解1. GROUP BY分组查询1.1 语句格式1.2 示例说明1.2.1 分别查询哥哥组和弟弟组的英语成绩总和1.2.2 查询哥哥组的所有成绩总和 2. 联合查询2.1 内连接2.1.1 语法格式2.1.2 执行过程 2.2 外连接2.2.1 左外连接2.2.2 右外连接 2.3 自连接2.…

《FMambaIR:一种基于混合状态空间模型和频域的方法用于图像恢复》学习笔记

paper&#xff1a;(PDF) FMambaIR: A Hybrid State Space Model and Frequency Domain for Image Restoration 目录 摘要 一、引言 二、相关工作 1、图像恢复 2、频率学习 3、状态空间模型&#xff08;SSM&#xff09; 三、框架 1、基本知识 2、整体框架 3、F-Mamba…

51c自动驾驶~合集47

我自己的原文哦~ https://blog.51cto.com/whaosoft/13083194 #DreamDrive 性能爆拉30%&#xff01;英伟达&#xff1a;时空一致下的生成重建大一统新方案~ 从自车的驾驶轨迹中生成真实的视觉图像是实现自动驾驶模型可扩展训练的关键一步。基于重建的方法从log中生成3D场景…

SpringBoot项目打war包要点

1. 修改pom文件packaging 2. 不使用内置tomcat 3. 加一个类 4. 修改外部tomcat配置文件 5. 修改nginx配置文件&#xff0c;构建集群 资料来自网络

GMM高斯混合聚类算法(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 GMM高斯混合聚类算法 matlab2023b语言&#xff0c;一键出图&#xff0c;直接运行 1.代码注释清晰&#xff0c;自行解读容易。 2…输出图例如图所示包括&#xff1a;聚类图(聚类结果图)&#xff0c;协方差矩阵类型…

华为数据中心CE系列交换机级联M-LAG配置示例

M-LAG组网简介 M-LAG&#xff08;Multi-chassis Link Aggregation&#xff09;技术是一种跨设备的链路聚合技术&#xff0c;它通过将两台交换机组成一个逻辑设备&#xff0c;实现链路的负载分担和故障切换&#xff0c;从而提高网络的可靠性和稳定性。下面给大家详细介绍如何在…

Vue2.0的安装

1.首先查看是否已经安装了node.js 选择以管理员方式打开命令提示符&#xff08;权限较高&#xff09;&#xff0c;或者通过cmd的方式打开 打开后输入node -v 查看自己电脑是否安装node&#xff0c;以及版本号 node -v 如果没有的话&#xff0c;请查看Node.js的安装 2.Vue和脚…

GAN 用于图像增强

工程需求&#xff0c;临时学一下gan的原理和基于图像增强的实现 原理 论文链接 Generative Adversarial Nets 我们提出了一个通过对抗过程来估计生成模型的新框架&#xff0c;其中我们同时训练两个模型&#xff1a;捕获数据分布的生成模型G和估计样本来自训练数据而不是G的…

sqlfather笔记

这里简单记录写学习鱼皮sqlfather项目的笔记&#xff0c;以供以后学习。 运行 将前后端项目clone到本地后&#xff0c;修改对应配置文件运行项目。 后端 1.配置好mysql后运行这个sql文件建立对应的表。 2.修改数据库密码 3.修改完后运行启动类即可 4. 启动结果 5.查看A…

【大数据】机器学习------支持向量机(SVM)

支持向量机的基本概念和数学公式&#xff1a; 1. 线性可分的支持向量机 对于线性可分的数据集 &#xff0c;其中(x_i \in R^d) 是特征向量 是类别标签&#xff0c;目标是找到一个超平面 &#xff0c;使得对于所有 的样本 &#xff0c;对于所有(y_i -1) 的样本&#xff0c;…

Android系统开发(一):AOSP 架构全解析:开源拥抱安卓未来

引言 当我们手握智能手机&#xff0c;流畅地滑动屏幕、切换应用、欣赏动画时&#xff0c;背后其实藏着一套庞大且精密的开源系统——Android AOSP&#xff08;Android Open Source Project&#xff09;。这套系统不仅是所有安卓设备的根基&#xff0c;也是系统开发者的终极 pl…

mono3d汇总

lidar坐标系 lidar坐标系可以简单归纳为标准lidar坐标系和nucense lidar坐标系&#xff0c;参考链接。这个坐标系和车辆的ego坐标系是一致的。 标准lidar坐标系 opendet3d&#xff0c;mmdetection3d和kitt都i使用了该坐标系 up z^ x front| /| /left y <------ 0kitti采…

linux下springboot项目nohup日志或tomcat日志切割处理方案

目录 1. 配置流程 2. 配置说明 其他配置选项&#xff1a; 3. 测试执行 4. 手动执行 https://juejin.cn/post/7081890486453010469 通常情况下&#xff0c;我们的springboot项目部署到linux服务器中&#xff0c;通过nohup java -jar xxx.jar &指令来进行后台运行我们…

[Python学习日记-78] 基于 TCP 的 socket 开发项目 —— 模拟 SSH 远程执行命令

[Python学习日记-78] 基于 TCP 的 socket 开发项目 —— 模拟 SSH 远程执行命令 简介 项目分析 如何执行系统命令并拿到结果 代码实现 简介 在Python学习日记-77中我们介绍了 socket 基于 TCP 和基于 UDP 的套接字&#xff0c;还实现了服务器端和客户端的通信&#xff0c;本…

使用SIPP发起媒体流性能测试详解

使用SIPP发起媒体流性能测试详解 一、SIPP工具简介二、测试前的准备三、编写测试脚本四、运行测试五、分析测试结果六、总结SIPP(SIP Performance Protocol)是一个开源工具,专门用于SIP(Session Initiation Protocol)协议的性能测试和基准测试。SIP是一种用于控制多媒体通…

macOS 安装JDK17

文章目录 前言介绍新特性下载安装1.下载完成后打开downloads 双击进行安装2.配置环境变量3.测试快速切换JDK 小结 前言 近期找开源软件&#xff0c;发现很多都已经使用JDK17springboot3 了&#xff0c;之前的JDK8已经被替换下场&#xff0c;所以今天就在本机安装了JDK17&#…