[Network Programming] How to make the UDP protocol more reliable

  • (? ), Hello, I am Youyan QAQ
  • My blog homepage: C/C++ language, data structure, Linux basics, ARM development board, network programming and other fields UP
  • Come on, learn together, and let us become a powerful siege lion!
  • A piece of chicken soup for myself and my readers: Concentrated will can break through stubborn rocks!
  • The author’s level is very limited. If you find any errors, please correct them in the comment area. Thank you

I encountered a question in an interview recently. When the interviewer asked about UDP, and asked how to make UDP more reliable, I only knew UDP. You can also retransmit, but what are the specific methods? I have never been exposed to it, so I will learn it today.

I think everyone, like me, has memorized the content of the UDP protocol. It is a connectionless oriented protocol. It provides high-performance data transmission in network communication, but does not guarantee data Reliability. Although UDP is very useful in some situations, in scenarios where reliability is required, we can adopt some strategies to increase the reliability of UDP transmission. This article will introduce these strategies, including timeout retransmission, orderly reception, response acknowledgment, and sliding window flow control.

1. Overview of UDP

UDP is a simple packet-oriented protocol that does not provide connection management, flow control or congestion control, so it is commonly used for real-time communications and multimedia streaming. However, since UDP does not guarantee the reliability of data packets, it may cause data packet loss or disorder in an unreliable network environment.

2. Strategies to increase UDP reliability

1. Timeout retransmission (timer)

Timeout retransmission is a basic mechanism that ensures that data packets reach the receiver within a limited time by setting a timer. If the timer expires and no reply is received, the sender will resend the packet.

Here is a simple C++ code example:

#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];

    //Create UDP socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Error creating socket");
        exit(1);
    }

    // Server address configuration
    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) {
        // send data
        const char* data = "Hello, UDP!";
        sendto(sockfd, data, strlen(data), 0, (struct sockaddr*) & amp;server_addr, server_len);

        //Set timeout
        struct timeval timeout;
        timeout.tv_sec = 2;
        timeout.tv_usec = 0;
        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) & amp;timeout, sizeof(timeout));

        //Receive response
        int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*) & amp;server_addr, & amp;server_len);
        if (n < 0) {
            std::cout << "Timeout, resend data..." << std::endl;
        } else {
            buffer[n] = '\0';
            std::cout << "Receive response from server:" << buffer << std::endl;
        }

        sleep(1); // Wait before sending next packet
    }

    return 0;
}

2. Orderly reception (add packet sequence number)

In order to solve the problem of out-of-order UDP data packets, we can add a packet sequence number to each data packet and sort the data packets according to the sequence number at the receiving end. This helps ensure that packets arrive at the receiver in the correct order.

The following is a sample C++ code:

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

    //Create UDP socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Error creating socket");
        exit(1);
    }

    // Server address configuration
    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*) & amp;server_addr, & amp;server_len);
        buffer[n] = '\0';
        int seq;
        memcpy( & amp;seq, buffer, sizeof(int));

        if (seq == expected_seq) {
            // Receive the expected data packet
            std::cout << "Receive data from server:" << (buffer + sizeof(int)) << std::endl;
            expected_seq + + ;
        }

        // send response
        sendto(sockfd, & amp;seq, sizeof(int), 0, (struct sockaddr*) & amp;server_addr, server_len);
    }

    return 0;
}

3. Response confirmation (Seq/Ack response mechanism)

The Seq/Ack response mechanism allows the receiver to send a response to the sender to confirm that the data packet has been successfully received. If the sender does not receive a reply, it may choose to retransmit the packet.

The following is a sample C++ code:

#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];
    intack = 0;

    //Create UDP socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Error creating socket");
        exit(1);
    }

    // Server address configuration
    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*) & amp;server_addr, & amp;server_len);
        buffer[n] = '\0';
        int seq;
        memcpy( & amp;seq, buffer, sizeof(int));

        if (seq == ack) {
            // Receive the expected data packet
            std::cout << "Receive data from server:" << (buffer + sizeof(int)) << std::endl;
            ack++;
        }

        // send response
        sendto(sockfd, & amp;ack, sizeof(int), 0, (struct sockaddr*) & amp;server_addr, server_len);
    }

    return 0;
}

4. Sliding window flow control and other mechanisms (sliding window protocol)

Sliding window protocols allow negotiation between the sender and receiver to control the flow and order of packets. This helps optimize transmission efficiency and reliability.

The following is a sample C++ code:

#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];
    intack = 0;
    int window_size = 5;
    int recv_buffer[window_size];

    //Create UDP socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Error creating socket");
        exit(1);
    }

    // Server address configuration
    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*) & amp;server_addr, & amp;server_len);
        buffer[n] = '\0';
        int seq;
        memcpy( & amp;seq, buffer, sizeof(int));

        if (seq == ack) {
            // Receive the expected data packet
            std::cout << "Receive data from server:" << (buffer + sizeof(int)) << std::endl;
            ack++;

            // Check subsequent packets
            for (int i = 0; i < window_size; i + + ) {
                int next_seq = ack + i;
                if (recv_buffer[next_seq] != 0) {
                    std::cout << "Receive data from server:" << recv_buffer[next_seq] << std::endl;
                    recv_buffer[next_seq] = 0;
                }
            }
        } else {
            // Store packets that arrive out of order
            recv_buffer[seq] = (buffer + sizeof(int));
        }

        // send response
        sendto(sockfd, & amp;ack, sizeof(int), 0, (struct sockaddr*) & amp;server_addr, server_len);
    }

    return 0;
}

3. Summary

Although UDP is a protocol that does not provide reliable transmission, we can increase the reliability of UDP transmission by implementing mechanisms such as timeout retransmission, orderly reception, response confirmation, and sliding window flow control. These strategies can be selected and combined according to the needs of specific applications to meet different reliability requirements. However, it should be noted that these mechanisms are implemented at the application layer, which introduces additional complexity and overhead, so TCP may still be a better choice for some applications that require high reliability.

For more articles related to C/C++ language, Linux system, data structure and ARM board practice, follow the column:

Hand-shredded C language

Play with linux

kick data structure

System and network programming

Explore C++

6818 (ARM) development board actual combat

Write it at the end

  • That’s it for today’s sharing~
  • I think the blogger’s writing is pretty good. One click and three consecutive links~
  • Thank you for your attention

The knowledge points of the article match the official knowledge archives, and you can further learn relevant knowledge. Network skill treeProtocols that support applicationsThe role of the application layer 42,310 people are learning the system