【网络编程】如何将UDP协议变得更可靠

news/2024/5/18 13:38:19 标签: c++, udp, 网络, 网络协议, visual studio code
  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,请在评论区指正,感谢🙏

ps:首先解释一下为什么没更新了,并不是我不学习了,而是招聘会和面试让我很难有时间坐下来打字,后续还是会更新的。


        近日面试中遇到一个问题,面试官问到UDP时问我如何使UDP变得更可靠,我只知道UDP也可以重传,但是具体如何实现呢未曾接触,于是今天分享一下。

        想必大家和我一样也背过UDP协议的内容,它是一种面向无连接的协议,它在网络通信中提供了高性能的数据传输,但不保证数据的可靠性。尽管UDP在某些情况下非常有用,但在需要可靠性的场景中,我们可以采用一些策略来增加UDP传输的可靠性。本文将介绍这些策略,包括超时重传、有序接收、应答确认和滑动窗口流量控制。

一、UDP概述

        UDP是一种简单的面向数据包的协议,它不提供连接管理、流控制或拥塞控制,因此通常被用于实时通信和多媒体流。但由于UDP不保证数据包的可靠性,它可能在不可靠网络环境下导致数据包丢失或乱序。

二、增加UDP可靠性的策略

1. 超时重传(定时器)

        超时重传是一种基本的机制,它通过设置定时器来确保数据包在有限时间内到达接收方。如果定时器超时并且没有接收到应答,发送方将重新发送数据包。

        下面是一个简单的C++代码示例:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[1024];

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("创建套接字出错");
        exit(1);
    }

    // 服务器地址配置
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    socklen_t server_len = sizeof(server_addr);

    while (true) {
        // 发送数据
        const char* data = "Hello, UDP!";
        sendto(sockfd, data, strlen(data), 0, (struct sockaddr*)&server_addr, server_len);

        // 设置超时
        struct timeval timeout;
        timeout.tv_sec = 2;
        timeout.tv_usec = 0;
        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

        // 接收响应
        int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len);
        if (n < 0) {
            std::cout << "超时,重新发送数据..." << std::endl;
        } else {
            buffer[n] = '\0';
            std::cout << "从服务器接收响应:" << buffer << std::endl;
        }

        sleep(1);  // 在发送下一个数据包之前等待
    }

    return 0;
}

2. 有序接收(添加包序号)

        为了解决UDP数据包的乱序问题,我们可以为每个数据包添加一个包序号,并在接收端按照序号对数据包进行排序。这有助于确保数据包以正确的顺序到达接收方。

        以下是一个示例C++代码:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[1024];
    int expected_seq = 0;

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("创建套接字出错");
        exit(1);
    }

    // 服务器地址配置
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    socklen_t server_len = sizeof(server_addr);

    while (true) {
        int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len);
        buffer[n] = '\0';
        int seq;
        memcpy(&seq, buffer, sizeof(int));

        if (seq == expected_seq) {
            // 接收到期望的数据包
            std::cout << "从服务器接收数据:" << (buffer + sizeof(int)) << std::endl;
            expected_seq++;
        }

        // 发送应答
        sendto(sockfd, &seq, sizeof(int), 0, (struct sockaddr*)&server_addr, server_len);
    }

    return 0;
}

3. 应答确认(Seq/Ack应答机制)

        Seq/Ack应答机制允许接收方向发送方发送应答,以确认已成功接收到数据包。如果发送方未收到应答,它可以选择重传数据包。

        以下是一个示例C++代码:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet_in.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[1024];
    int ack = 0;

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("创建套接字出错");
        exit(1);
    }

    // 服务器地址配置
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    socklen_t server_len = sizeof(server_addr);

    while (true) {
        int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len);
        buffer[n] = '\0';
        int seq;
        memcpy(&seq, buffer, sizeof(int));

        if (seq == ack) {
            // 接收到期望的数据包
            std::cout << "从服务器接收数据:" << (buffer + sizeof(int)) << std::endl;
            ack++;
        }

        // 发送应答
        sendto(sockfd, &ack, sizeof(int), 0, (struct sockaddr*)&server_addr, server_len);
    }

    return 0;
}

4. 滑动窗口流量控制等机制(滑动窗口协议)

        滑动窗口协议允许发送方和接收方之间协商,以控制数据包的流量和顺序。这有助于优化传输的效率和可靠性。

        以下是一个示例C++代码:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[1024];
    int ack = 0;
    int window_size = 5;
    int recv_buffer[window_size];

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("创建套接字出错");
        exit(1);
    }

    // 服务器地址配置
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    socklen_t server_len = sizeof(server_addr);

    while (true) {
        int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &server_len);
        buffer[n] = '\0';
        int seq;
        memcpy(&seq, buffer, sizeof(int));

        if (seq == ack) {
            // 接收到期望的数据包
            std::cout << "从服务器接收数据:" << (buffer + sizeof(int)) << std::endl;
            ack++;

            // 检查后续数据包
            for (int i = 0; i < window_size; i++) {
                int next_seq = ack + i;
                if (recv_buffer[next_seq] != 0) {
                    std::cout << "从服务器接收数据:" << recv_buffer[next_seq] << std::endl;
                    recv_buffer[next_seq] = 0;
                }
            }
        } else {
            // 存储未按顺序到达的数据包
            recv_buffer[seq] = (buffer + sizeof(int));
        }

        // 发送应答
        sendto(sockfd, &ack, sizeof(int), 0, (struct sockaddr*)&server_addr, server_len);
    }

    return 0;
}

三、总结

        尽管UDP是一种不提供可靠性传输的协议,但通过实现超时重传、有序接收、应答确认和滑动窗口流量控制等机制,我们可以增加UDP传输的可靠性。这些策略可以根据具体应用的需求来选择和组合,以满足不同的可靠性要求。然而,需要注意的是,这些机制在应用层实现,会引入额外的复杂性和开销,因此对于某些需要高度可靠性的应用,TCP可能仍然是更好的选择

        更多C/C++语言Linux系统数据结构ARM板实战相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            系统、网络编程

                                     探索C++

                                             6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉🎉🎉感谢关注🎉🎉🎉

http://www.niftyadmin.cn/n/5161073.html

相关文章

Vue项目图片预览v-viewer插件使用

v-viewer 是一个基于 Vue.js 的图片查看器组件&#xff0c;它允许你在项目中轻松地实现图片的查看和放大功能。这个组件提供了一种用户友好的方式来查看单个图片或者图片集合&#xff0c;支持放大、缩小、旋转、全屏显示等功能。在项目中使用 v-viewer 非常简单&#xff0c;下面…

kilo TextEditor-4

文章目录 语法高亮显示数字的颜色重构语法高亮显示搜索结果添加颜色搜索后恢复高亮多彩的数字检测文件类型有颜色的字符串有颜色的单行注释有颜色的关键字不可打印的字符有颜色的多行注释 语法高亮显示 数字的颜色 void editorDrawRows(struct abuf *ab) {int y;for (y 0; y…

Android 12 S 系统开机流程分析 - SetupSelinux(二)

本文接着上文开始讲解&#xff0c;上文中最后一步执行后会执行init启动过程中的第二步SetupSelinux(Selinux配置阶段)&#xff0c;这样又会走到main.cpp中的main方法。 1. SetupSelinux 由于上一篇中最后一步在重新执行init的时候携带了参数selinux_setup&#xff0c;所以此处…

ModuleNotFoundError: No module named ‘torchvision.models.utils‘

如图报错&#xff1a;No module named torchvision.models.utils解决方案&#xff1a;由于当前python环境高版本的torch&#xff0c; 代码用的是低版本的语法 将 from torchvision.models.utils import load_state_dict_from_url换成 from torch.hub import load_state_dict_fr…

探秘Python闭包与作用域

文章目录 闭包的定义与作用LEGB规则nonlocal与global关键字在Python的世界里,理解闭包(Closure)和作用域(Scope)是提升编程技巧和深度的一大步。这篇文章将带你深入了解闭包的神秘面纱,掌握LEGB规则,并使用nonlocal与global关键字来巧妙控制变量作用域。 闭包的定义与作…

提升ChatGPT答案质量和准确性的方法Prompt专家

文章目录 1、提供示例2、分步推理3、表格格式4、prompt转换器5、批判性提示6、比较提示7、逆向提示生成器1、提供示例 当你想模仿 某个事物的时候,比如:文案/风格/语气/语法的时候,模仿李白、马云、马斯克 当你想复制 一种难以明确描述,抽象形式的时候; 我们为chatgpt提供…

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远程控制软件总结

红队专题 招募六边形战士队员[30]远控班第一期课程与远控总结 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 [30]远控班第一期课程与远控总结 一.Bug修复(1)生成路径(2)显示系统版本号二.内存泄露(1)如何检查内存泄露 #define CRTDBG_…

Mysql进阶-视图篇

介绍 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&#xff0c;不保存查询结果。…