网络编程 | 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 8080

int 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; // IPv4
    servaddr.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;
}

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

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

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

相关文章

A5.Springboot-LLama3.2服务自动化构建(二)——Jenkins流水线构建配置初始化设置

下面我们接着上一篇文章《A4.Springboot-LLama3.2服务自动化构建(一)——构建docker镜像配置》继续往下分析,在自动化流水线构建过程当中的相关初始化设置和脚本编写。 一、首先需要先安装Jenkins 主部分请参考我前面写的一篇文章《Jenkins持续集成与交付安装配置》 二、…

开发神器之cursor

文章目录 cursor简介主要特点 下载cursor页面的简单介绍切换大模型指定ai学习的文件指定特定的代码喂给ai创建项目框架文件 cursor简介 Cursor 是一款专为开发者设计的智能代码编辑器&#xff0c;集成了先进的 AI 技术&#xff0c;旨在提升编程效率。以下是其主要特点和功能&a…

基于机器学习随机森林算法的个人职业预测研究

1.背景调研 随着信息技术的飞速发展&#xff0c;特别是大数据和云计算技术的广泛应用&#xff0c;各行各业都积累了大量的数据。这些数据中蕴含着丰富的信息和模式&#xff0c;为利用机器学习进行职业预测提供了可能。机器学习算法的不断进步&#xff0c;如深度学习、强化学习等…

【王树森搜索引擎技术】概要01:搜索引擎的基本概念

1. 基本名词 query&#xff1a;查询词SUG&#xff1a;搜索建议文档&#xff1a;搜索结果标签/筛选项 文档单列曝光 文档双列曝光 2. 曝光与点击 曝光&#xff1a;用户在搜索结果页上看到文档&#xff0c;就算曝光文档点击&#xff1a;在曝光后&#xff0c;用户点击文档&…

图论DFS:黑红树

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法&#xff1a;记忆化搜索DFS 算法&#xf…

ros2-7.5 做一个自动巡检机器人

7.5.1 需求及设计 又到了小鱼老师带着做最佳实践项目了。需求&#xff1a;做一个在各个房间不断巡逻并记录图像的机器人。 到达目标点后首先通过语音播放到达目标点信息&#xff0c; 再通过摄像头拍摄一张图片保存到本地。 7.5.2 编写巡检控制节点 在chapt7_ws/src下新建功…

告别繁琐编译!make和makefile的便捷之道

Linux系列 文章目录 Linux系列前言一、make/makefile是什么&#xff1f;二、make/makefile的使用2.1、语法规则2.2、依赖关系和依赖方法2.3、清理可执行文件2.4、执行依据 三、循环依赖问题总结 前言 上一篇博客给大家分享了在Linux下编译源代码的两个工具&#xff0c;gcc和g…

【鸿蒙】0x02-LiteOS-M基于Qemu RISC-V运行

OpenHarmony LiteOS-M基于Qemu RISC-V运行 系列文章目录更新日志OpenHarmony技术架构OH技术架构OH支持系统类型轻量系统&#xff08;mini system&#xff09;小型系统&#xff08;small system&#xff09;标准系统&#xff08;standard system&#xff09; 简介环境准备安装QE…

C语言初阶习题【29】杨氏矩阵

1. 题目描述——杨氏矩阵 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N); 2. 思路 3. 代码实现1 #include<stdio.h>void fin…

Cloud Foundry,K8S,Mesos Marathon弹性扩缩容特性对比

一、Cloud Foundry 使用Scaling an Application Using App Autoscaler插件&#xff0c;基于资源使用情况触发简单扩缩容 CPU、内存、Http带宽、延时等 监控这些资源的使用情况决定扩缩容策略&#xff1a;实例是增加还是减少 Instance Limits 限制实例数量范围&#xff0c;定义…

中职网络建设与运维ansible服务

ansible服务 填写hosts指定主机范围和控制节点后创建一个脚本&#xff0c;可以利用简化脚本 1. 在linux1上安装系统自带的ansible-core,作为ansible控制节点,linux2-linux7作为ansible的受控节点 Linux1 Linux1-7 Yum install ansible-core -y Vi /etc/ansible/hosts 添加…

【BUUCTF】[GXYCTF2019]BabySQli

进入页面如下 尝试万能密码注入 显示这个&#xff08;qyq&#xff09; 用burp suite抓包试试 发现注释处是某种编码像是base编码格式 MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5 可以使用下面这个网页在线工具很方便…

迅为瑞芯微RK3562开发板/核心板应用于人脸跟踪、身体跟踪、视频监控、自动语音识别(ASR)、图像分类驾驶员辅助系统(ADAS)...

可应用于人脸跟踪、身体跟踪、视频监控、自动语音识别(ASR)、图像分类驾驶员辅助系统(ADAS)、车牌识别、物体识别等。iTOP-3562开发板/核心板采用瑞芯微RK3562处理器&#xff0c;内部集成了四核A53Mali G52架构&#xff0c;主频2GHZ&#xff0c;内置1TOPSNPU算力&#xff0c;RK…

蓝桥杯单片机基础部分——5、DS18B20温度传感器

前言 好久没有更新关于蓝桥杯单片机相关的模块了&#xff0c;今天更新一下数字温度传感器DS18B20的相关应用 单线数字温度计DS1820介绍 DS1820数字温度计提供9位(二进制)温度读数&#xff0c;指示器件的温度。信息经过单线接口送入DS1820 或从 DS1820 送出&#xff0c;因此从…

python爬虫入门(实践)

python爬虫入门&#xff08;实践&#xff09; 一、对目标网站进行分析 二、博客爬取 获取博客所有h2标题的路由 确定目标&#xff0c;查看源码 代码实现 """ 获取博客所有h2标题的路由 """url "http://www.crazyant.net"import re…

nginx 配置代理,根据 不同的请求头进行转发至不同的代理

解决场景&#xff1a;下载发票的版式文件&#xff0c;第三方返回的是url链接地址&#xff0c;但是服务是部署在内网环境&#xff0c;无法访问互联网进行下载。此时需要进行走反向代理出去&#xff0c;如果按照已有套路&#xff0c;就是根据不同的访问前缀&#xff0c;跳转不同的…

EI Scopus双检索 | 2025年第四届信息与通信工程国际会议(JCICE 2025)

会议简介 Brief Introduction 2025年第四届信息与通信工程国际会议(JCICE 2025) 会议时间&#xff1a;2025年7月25日-27日 召开地点&#xff1a;中国哈尔滨 大会官网&#xff1a;www.jcice.org 由黑龙江大学和成都信息工程大学主办&#xff0c;江苏科技大学协办的2025年第四届信…

软考高级5个资格、中级常考4个资格简介及难易程度排序

一、软考高级5个资格 01、网络规划设计师 资格简介&#xff1a;网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力&#xff1b;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介&#xff1a;系统分…

STM32 FreeRTOS 任务挂起和恢复---实验

实验目标 学会vTaskSuspend( )、vTaskResume( ) 任务挂起与恢复相关API函数使用&#xff1a; start_task:用来创建其他的三个任务。 task1&#xff1a;实现LED1每500ms闪烁一次。 task2&#xff1a;实现LED2每500ms闪烁一次。 task3&#xff1a;判断按键按下逻辑&#xff0c;KE…

YOLO系列代码

Test-Time Augmentation TTA (Test Time Augmentation)是指在test过程中进行数据增强。其思想非常简单&#xff0c;就是在评测阶段&#xff0c;给每个输入进行多种数据增广变换&#xff0c;将一个输入变成多个输入&#xff0c;然后再merge起来一起输出&#xff0c;形成一种ens…