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

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


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

【网络编程】如何将UDP协议变得更可靠_第1张图片

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

一、UDP概述

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

二、增加UDP可靠性的策略

1. 超时重传(定时器)

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

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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)开发板实战

写在最后

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

你可能感兴趣的:(网络编程,c++,udp,网络,网络协议,visual,studio,code)