Linux网络编程系列之UDP协议编程

news2024/12/25 12:43:17

一、什么是UDP协议

        UPD协议(User Datagram Protocol,用户数据报协议)是Internet协议族中的一个无连接协议,属于传输层,它不保证数据传输的可靠性或完整性,只是把应用程序发给网络层的数据封装成数据包进行传输。

二、特性

        1、无连接性:UDP协议不需要在发送数据之前建立连接,也不需要在传输过程中维持连接状态。

        2、数据报式:UDP协议把应用程序发送的数据封装成报文(数据报)进行传输,每个报文都是独立的,独立传输,不关心前后次序的。

        3、快速性:UDP协议没有传输确认和重传机制,数据传输快速,适用于实时应用。

        4、轻量级:UDP协议的头部信息较小,占用网络带宽较少。

        5、不可靠性:UDP协议不保证数据传输的可靠性和完整性,数据包发送后无法得到确认,也无法保证数据包能否到达目标地点。

        6、丢包率高:UDP协议传输的数据包容易被网络拥塞、路由错误或网络故障等因素导致丢失。(需要特别注意,丢包是否需要重传)

三、使用场景

        1、视频及音频应用:UDP协议适合用于实时视频和音频传输应用,如视频会议、网络电视、实时语音对话等。

        2、游戏应用:在线游戏对实时性的要求非常高,因此很多游戏都采用UDP协议来传输游戏数据,如游戏画面、玩家动作、游戏状态等。

        3、广播:UDP协议支持广播和多播,适用于局域网内广播数据等应用场景。

        4、简单请求-响应应用:UDP协议适用于一些对数据传输可靠性要求不高,但是需要快速取得响应的应用,如DNS、NTP等。

        5、IoT应用:在物联网应用场景下,由于传输数据量小,且对实时性要求较高,UDP协议被广泛应用于传输数据。

        总之,UDP协议适用于对数据传输可靠性要求不高,但对实时性要求较高的场景,能够提供快速的数据传输和较低的网络延迟。

四、C/S架构UDP通信流程

        1、客户端

        (1)、建立套接字。使用socket()函数

        (2)、设置端口复用。使用setsockopt()函数,这一步可选(推荐) 

        (3)、绑定自己的IP地址和端口号。使用bind()函数,这一步可以省略

        (4)、和UDP服务器进行收发数据。使用sendto()发送,recvfrom()接收

        (5)、关闭套接字。使用close()

        2、服务端

        (1)、建立套接字。使用socket()函数

        (2)、设置端口复用。使用setsockopt()函数,这一步可选(推荐) 

        (3)、绑定自己的IP地址和端口号。使用bind()函数,这一步不可以省略!

        (4)、和客户端进行收发数据。使用sendto()发送,recvfrom()接收

        (5)、关闭套接字。使用close()

五、相关函数API接口

        1、建立套接字

// 建立套接字 
int socket(int domain, int type, int protocol);

// 接口说明
        返回值:成功返回一个套接字文件描述符,失败返回-1

        参数domain:用来指定使用何种地址类型,有很多,具体看别的资源
            (1)PF_INET 或者 AF_INET 使用IPV4网络协议
            (2)其他很多的,看别的资源

        参数type:通信状态类型选择,有很多,具体看别的资源
            (1)SOCK_STREAM    提供双向连续且可信赖的数据流,即TCP
            (2)SOCK_DGRAM     使用不连续不可信赖的数据包连接,即UDP
    
        参数protocol:用来指定socket所使用的传输协议编号,通常不用管,一般设为0

        2、设置端口状态

// 设置端口的状态
int setsockopt(int sockfd, 
               int level, 
               int optname,
               const void *optval, 
               socklen_t optlen);


// 接口说明
        返回值:成功返回0,失败返回-1
        参数sockfd:待设置的套接字

        参数level: 待设置的网络层,一般设成为SOL_SOCKET以存取socket层

        参数optname:待设置的选项,有很多种,具体看别的资源,这里讲常用的
            (1)、SO_REUSEADDR    允许在bind()过程中本地地址可复用,即端口复用
            (2)、SO_BROADCAST    使用广播的方式发送,通常用于UDP广播
            (3)、SO_SNDBUF       设置发送的暂存区大小
            (4)、SO_RCVBUF       设置接收的暂存区大小

        参数optval:待设置的值

        参数optlen:参数optval的大小,即sizeof(optval)

        3、绑定自己的IP地址和端口号

// 绑定自己的IP地址和端口号
 int bind(int sockfd, 
          const struct sockaddr *addr,
          socklen_t addrlen);

// 接口说明
        返回值:
        参数sockfd:待绑定的套接字

        参数addrlen:参数addr的大小,即sizeof(addr)

        参数addr:IP地址和端口的结构体,通用的结构体,根据sockfd的类型有不同的定义
        当sockfd的domain参数指定为IPV4时,结构体定义为
            struct sockaddr_in
            {
                unsigned short int sin_family;    // 需与sockfd的domain参数一致
                uint16_t sin_port;            // 端口号
                struct in_addr sin_addr;      // IP地址 
                unsigned char sin_zero[8];    // 保留的,未使用
            };
            struct in_addr
            {
                uin32_t s_addr;
            }
// 注意:网络通信时,采用大端字节序,所以端口号和IP地址需要调用专门的函数转换成网络字节序
    

        4、字节序转换接口 

// 第一组接口
// 主机转网络IP地址,输入主机IP地址
uint32_t htonl(uint32_t hostlong);

// 主机转网络端口,输入主机端口号
uint16_t htons(uint16_t hostshort);    // 常用

// 网络转主机IP,输入网络IP地址
uint32_t ntohl(uint32_t netlong);

// 网络转主机端口,输入网络端口
uint16_t ntohs(uint16_t netshort);


// 第二组接口,只能用于IPV4转换,IP地址
// 主机转网络
int inet_aton(const char *cp, struct in_addr *inp);

// 主机转网络
in_addr_t inet_addr(const char *cp);    // 常用

// 网络转主机
int_addr_t inet_network(const char *cp);

// 网络转主机
char *inet_ntoa(struct in_addr in);    // 常用

        5、发送数据

// UDP协议发送数据
ssize_t sendto(int sockfd, 
               const void *buf, 
               size_t len, 
               int flags,
               const struct sockaddr *dest_addr, 
               socklen_t addrlen);

// 接口说明
        返回值:成功返回成功发送的字节数,失败返回-1
        参数sockfd:发送者的套接字
        参数buf:发送的数据缓冲区
        参数len:发送的长度
        参数flags:一般设置为0,还有其他数值,具体查询别的资源
        参数dest_addr:接收者的网络地址
        参数addrlen:接收者的网络地址大小,即sizeof(dest_addr)
    

        6、接收数据

// UDP协议接收数据
ssize_t recvfrom(int sockfd, 
                 void *buf, 
                 size_t len, 
                 int flags, 
                 struct sockaddr *src_addr, 
                 socklen_t *addrlen);

// 接口说明:
        返回值:成功返回成功接收的字节数,失败返回-1
        参数sockfd:接收者的套接字
        参数buf:接收数据缓的冲区
        参数len:接收的最大长度
        参数flags:一般设置为0,还有其他数值,具体查询别的资源
        参数src_addr:发送者的网络地址,可以设置为NULL
        参数addrlen:  发送者的网络地址大小,即sizeof(src_addr)

        7、关闭套接字

// 关闭套接字
int close(int fd);

// 接口说明
        返回值:成功返回0,失败返回-1
        参数fd:套接字文件描述符

六、案例

        使用UDP协议完成C/S架构的客户端和服务端通信演示

        客户端UdpClient.c

// UDP客户端的案例

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

#define CLIENT_IP   "192.168.64.128"    // 记得改为自己IP
#define CLIENT_PORT 10000   // 不能超过65535,也不要低于1000,防止端口误用

int main(int argc, char *argv[])
{
    // 1、建立套接字,使用IPV4网络地址,UDP协议
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1)
    {
        perror("socket fail");
        return -1;
    }
    
    // 2、设置端口复用(推荐)
    int optval = 1; // 这里设置为端口复用,所以随便写一个值
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    if(ret == -1)
    {
        perror("setsockopt fail");
        close(sockfd);
        return -1;
    }

    // 3、绑定自己的IP地址和端口号(可以省略)
    struct sockaddr_in client_addr = {0};
    socklen_t addr_len = sizeof(struct sockaddr);
    client_addr.sin_family = AF_INET;   // 指定协议为IPV4地址协议
    client_addr.sin_port = htons(CLIENT_PORT);  // 端口号
    client_addr.sin_addr.s_addr = inet_addr(CLIENT_IP); // IP地址

    ret = bind(sockfd, (struct sockaddr*)&client_addr, addr_len);
    if(ret == -1)
    {
        perror("bind fail");
        close(sockfd);
        return -1;
    }

    // 4、收发数据
    uint16_t port = 0;  // 端口号
    char ip[20] = {0};  // IP地址
    struct sockaddr_in server_addr = {0};

    char msg[128] = {0};    // 数据缓冲区
    pid_t pid = fork();
    // 父进程发送数据
    if(pid > 0)
    {
        printf("please input receiver IP and port\n");
        scanf("%s %hd", ip, &port);
        printf("IP = %s, port = %hd\n", ip, port);
        server_addr.sin_family = AF_INET;   // 指定用IPV4地址
        server_addr.sin_port = htons(port); // 接收者的端口号
        server_addr.sin_addr.s_addr = inet_addr(ip);    // 接收者的IP地址
        
        while(getchar() != '\n');   // 清空多余的换行符
        while(1)
        {
            printf("please input data:\n");
            fgets(msg, sizeof(msg)/sizeof(msg[0]), stdin);

            // 发送数据,注意要填写接收者的地址
            ret = sendto(sockfd, msg, strlen(msg), 0, 
                (struct sockaddr*)&server_addr, addr_len);
            if(ret > 0)
            {
                printf("success: send %d bytes\n", ret);
            }
        }
    }

    // 子进程接收数据
    else if(pid == 0)
    {
        while(1)
        {
            // 接收数据,注意使用发送者的地址来接收
            ret = recvfrom(sockfd, msg, sizeof(msg)/sizeof(msg[0]), 0, 
                (struct sockaddr*)&server_addr, &addr_len);
            if(ret > 0)
            {
                memset(ip, 0, sizeof(ip));  // 先清空IP
                strcpy(ip, inet_ntoa(server_addr.sin_addr));    // 网络IP转主机IP
                port = ntohs(server_addr.sin_port); // 网络端口号转主机端口号

                printf("[%s:%d] send data: %s\n", ip, port, msg);
                memset(msg, 0, sizeof(msg));    // 清空数据区
            }
        }
    }
    else
    {
        perror("fork fail");
        close(sockfd);
        return -1;
    }
    
    // 5、关闭套接字
    close(sockfd);

    return 0;
}

        服务端UdpServer.c

// UDP服务端的案例

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

#define SERVER_IP   "192.168.64.128"    // 记得改为自己IP
#define SERVER_PORT 20000   // 不能超过65535,也不要低于1000,防止端口误用

int main(int argc, char *argv[])
{
    // 1、建立套接字,使用IPV4网络地址,UDP协议
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1)
    {
        perror("socket fail");
        return -1;
    }
    
    // 2、设置端口复用(推荐)
    int optval = 1; // 这里设置为端口复用,所以随便写一个值
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    if(ret == -1)
    {
        perror("setsockopt fail");
        close(sockfd);
        return -1;
    }

    // 3、绑定自己的IP地址和端口号(不能省略)
    struct sockaddr_in server_addr = {0};
    socklen_t addr_len = sizeof(struct sockaddr);
    server_addr.sin_family = AF_INET;   // 指定协议为IPV4地址协议
    server_addr.sin_port = htons(SERVER_PORT);  // 端口号
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); // IP地址

    ret = bind(sockfd, (struct sockaddr*)&server_addr, addr_len);
    if(ret == -1)
    {
        perror("bind fail");
        close(sockfd);
        return -1;
    }

    // 4、收发数据
    uint16_t port = 0;  // 端口号
    char ip[20] = {0};  // IP地址
    struct sockaddr_in client_addr = {0};

    char msg[128] = {0};    // 数据缓冲区
    pid_t pid = fork();
    // 父进程发送数据
    if(pid > 0)
    {
        printf("please input receiver IP and port\n");
        scanf("%s %hd", ip, &port);
        printf("IP = %s, port = %hd\n", ip, port);
        client_addr.sin_family = AF_INET;   // 指定用IPV4地址
        client_addr.sin_port = htons(port); // 接收者的端口号
        client_addr.sin_addr.s_addr = inet_addr(ip);    // 接收者的IP地址
        
        while(getchar() != '\n');   // 清空多余的换行符
        while(1)
        {
            printf("please input data:\n");
            fgets(msg, sizeof(msg)/sizeof(msg[0]), stdin);

            // 发送数据,注意要填写接收者的地址
            ret = sendto(sockfd, msg, strlen(msg), 0, 
                (struct sockaddr*)&client_addr, addr_len);
            if(ret > 0)
            {
                printf("sccuess: send %d bytes\n", ret);
            }
        }
    }

    // 子进程接收数据
    else if(pid == 0)
    {
        while(1)
        {
            // 接收数据,注意使用发送者的地址来接收
            ret = recvfrom(sockfd, msg, sizeof(msg)/sizeof(msg[0]), 0, 
                (struct sockaddr*)&client_addr, &addr_len);
            if(ret > 0)
            {
                memset(ip, 0, sizeof(ip));  // 先清空IP
                strcpy(ip, inet_ntoa(client_addr.sin_addr));    // 网络IP转主机IP
                port = ntohs(client_addr.sin_port); // 网络端口号转主机端口号

                printf("[%s:%d] send data: %s\n", ip, port, msg);
                memset(msg, 0, sizeof(msg));    // 清空数据区
            }
        }
    }
    else
    {
        perror("fork fail");
        close(sockfd);
        return -1;
    }
    
    // 5、关闭套接字
    close(sockfd);

    return 0;
}

        通信演示

 

         注:上面只是对C/S架构UDP通信流程进行演示,并没有做什么丢包重传的检测,以上演示还介绍了一些测试命令用来建立一个UDP服务端

七、总结

        UDP协议是属于传输层的,适用于一些对数据传输的实时性要求高、但对数据传输的可靠性要求较低的场景,如视频会议等流媒体应用。注意要考虑UDP协议丢包的问题,是否需要重传。UDP协议下的客户端通信流程和服务器通信流程大致一致,可以结合案例加深对UDP协议的理解。

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

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

相关文章

VL53L5CX驱动开发(1)----驱动TOF进行区域检测

VL53L5CX驱动开发----1.驱动TOF进行区域检测 闪烁定义视频教学样品申请源码下载主要特点硬件准备技术规格系统框图应用示意图区域映射生成STM32CUBEMX选择MCU 串口配置IIC配置X-CUBE-TOF1串口重定向代码配置Tera Term配置演示结果 闪烁定义 VL53L5CX是一款先进的飞行感应&…

【C语言】利用数组处理批量数据(字符数组)

前言:前面已经介绍了&#xff0c;字符数据是以字符的ASCII代码存储在存储单元中的&#xff0c;一般占一个字节。由于ASCII代码也属于整数形式&#xff0c;因此在C99标准中&#xff0c;把字符类型归纳为整型类型中的一种。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x…

使用ebpf 监控linux内核中的nat转换

1.简介 Linux NAT&#xff08;Network Address Translation&#xff09;转换是一种网络技术&#xff0c;用于将一个或多个私有网络内的IP地址转换为一个公共的IP地址&#xff0c;以便与互联网通信。 在k8s业务场景中&#xff0c;业务组件之间的关系十分复杂. 由于 Kubernete…

浅谈在操控器类中,为何要通过osgGA::CameraManipulator的逆矩阵改变视点位置

在osg代码目录下的include\osgGA目录存放了很多osg自带的操控器类&#xff0c;这些操控器类都派生自osgGA::CameraManipulator&#xff0c;而这个CameraManipulator又派生自osgGA::GUIEventHandler&#xff0c;可见其本质上是个事件处理类。因此它首先会接收事件&#xff0c;比…

月薪20k的软件测试工程师都要具备什么能力?你跟大佬的差距在哪?

第一&#xff0c;强大的业务能力&#xff1a;很熟悉业务流程&#xff0c;熟悉业务模块、数据、架构&#xff0c;测试所需资源。了解测试所需时间。 第二&#xff0c;发现bug能力&#xff1a;一般问题发现的能力&#xff0c;隐性问题发现能力&#xff0c;连带问题发现能力&…

专为实现最高性能和效率而设计,SQN3242UCKGTA、SQN3220SC、SQN3220 LTE-A Cat 6 模块【SKY85735-11射频前端】

一、SQN3242UCKGTA、SQN3220SC、SQN3220 LTE-A Cat 6 模块 1、简介 Sequans 的 Cassiopeia 是 Cat4 和 Cat6 LTE-Advanced 平台系列&#xff0c;包括集成了高性能网络和应用 CPU 的 SQN3220 Cat6 基带 SoC 和 SQN3220SC Cat4 基带 SoC、Sequans 的 SQN3242 LTE 优化收发器、经…

Pyhon-每日一练(1)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

大华智慧园区前台任意文件上传(1day)

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 漏洞简介 大华智慧园区综合管理平台是一个集智能化、信息化、网络化、…

3D人脸生成的论文

一、TECA 1、论文信息 2、开源情况&#xff1a;comming soon TECA: Text-Guided Generation and Editing of Compositional 3D AvatarsGiven a text description, our method produces a compositional 3D avatar consisting of a mesh-based face and body and NeRF-based ha…

总结三:计算机网络面经

文章目录 1、简述静态路由和动态路由&#xff1f;2、说说有哪些路由协议&#xff0c;都是如何更新的&#xff1f;3、简述域名解析过程&#xff0c;本机如何干预域名解析&#xff1f;4、简述 DNS 查询服务器的基本流程是什么&#xff1f;DNS 劫持是什么&#xff1f;5、简述网关的…

CCS安装和运行TMS320F28004x第一个程序

1. CCS安装 TI 的MCU或者DSP&#xff0c;官方的集成开发环境是 Code Composer Studio™ &#xff0c;要开发TI的芯片&#xff0c;首先需要安装 CCS 环境。 CCS 软件可以到下面的 TI 官网下载&#xff1a; https://www.ti.com.cn/tool/cn/CCSTUDIO 下载完之后&#xff0c;点击…

Pandas vs SQL全面对比

前一段时间给大家详解过 Pandas 的用法&#xff0c;今天再来分享下 Pandas 与 SQL 的对比。 Pandas 和 SQL 有很多相似之处&#xff0c;都是对二维表的数据进行查询、处理&#xff0c;都是数据分析中常用的工具。 对于只会 Pandas 或只会 SQL 的朋友&#xff0c;可以通过今天…

【QT5-程序控制电源-RS232-SCPI协议-上位机-基础样例【1】】

【QT5-程序控制电源-RS232-SCPI协议-上位机-基础样例【1】】 1、前言2、实验环境3、自我总结1、基础了解仪器控制-熟悉仪器2、连接SCPI协议3、选择控制方式-程控方式-RS2324、代码编写 4、熟悉协议-SCPI协议5、测试实验-测试指令&#xff08;1&#xff09;硬件连接&#xff08;…

学习记忆——图像篇——记忆古诗词

《长歌行》 青青园中葵&#xff0c;朝露待日晞。 阳春布德泽&#xff0c;万物生光辉。 常恐秋节至&#xff0c;焜黄华叶衰。 百川东到海&#xff0c;何时复西归&#xff1f; 少壮不努力&#xff0c;老大徒伤悲&#xff01;

wisemodel 始智AI - 小记

文章目录 关于 wisemodel 始智AI 关于 wisemodel 始智AI https://www.wisemodel.cn/home 旨在打造中国版 “HuggingFace” 该社区汇聚了清华 / 智谱 chatglm2-6B、Stable Diffusion V1.5、alphafold2、seamless m4t large 等模型&#xff0c;以及 shareGPT、ultrachat、moss-…

80%测试员被骗,关于jmeter 的一个弥天大谎!

jmeter是目前大家都喜欢用的一款性能测试工具&#xff0c;因为它小巧、简单易上手&#xff0c;所以很多人都愿意用它来做接口测试或者性能测试&#xff0c;因此&#xff0c;在目前企业中&#xff0c;使用各个jmeter的版本都有&#xff0c;其中以jmeter3.x、4.x的应该居多。 但是…

网络安全行业真的内卷了吗?网络安全就业必看

前言 有一个特别流行的词语叫做“内卷”&#xff1a; 城市内卷太严重了&#xff0c;年轻人不好找工作&#xff1b;教育内卷&#xff1b;考研内卷&#xff1b;当然还有计算机行业内卷…… 这里的内卷当然不是这个词原本的意思&#xff0c;而是“过剩”“饱和”的替代词。 按照…

c++ 学习 之 运算符重载 知识要点

我们要好好分清楚一些运算符的结果为 左值还是 右值 赋值与调用

5. Mysql卸载

Mysql卸载 已经成功安装mysql&#xff0c;没有必要卸载&#xff0c;卸载之后不一定再次会安装成功。 双击安装包 检查如下三个目录是否有mysql&#xff0c;有的话&#xff0c;删除掉即可&#xff08;前提&#xff0c;电脑只有Mysql8,否则mysql其他版本也会被删除&#xff09;…

c#设计模式-行为型模式 之 状态模式

&#x1f680;简介 状态模式是一种行为设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为&#xff0c;我们可以通过创建一个状态接口和一些实现了该接口的状态类来实现状态模式。然后&#xff0c;我们可以创建一个上下文类&#xff0c;它会根据其当前的状态对象来改…