MST

星途 面试题库

面试题:C语言UDP通信中的数据可靠性保障

UDP是无连接的不可靠传输协议,在Linux C语言UDP通信应用中,如果需要一定程度上保障数据的可靠性传输,你会采取哪些措施?请从代码实现层面详细阐述,比如如何设计重传机制、如何处理数据包的顺序等。
15.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 重传机制

在Linux C语言UDP通信中实现重传机制,一般步骤如下:

  1. 设置定时器:使用setitimer函数设置定时器,在指定时间后触发信号。例如:
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>

void timeout_handler(int signum) {
    // 重传逻辑
    printf("Timeout, retransmit data...\n");
}

int main() {
    struct itimerval timer;
    struct sigaction sa;

    // 初始化信号处理函数
    sa.sa_handler = timeout_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    // 设置定时器
    timer.it_value.tv_sec = 1;  // 首次触发时间1秒
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 0; // 不重复触发
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);

    // 其他UDP通信代码
    //...

    return 0;
}
  1. 记录发送时间:在发送数据时记录当前时间戳。可以定义一个结构体来存储数据和发送时间:
typedef struct {
    char data[1024];
    struct timeval send_time;
} Packet;
  1. 重传逻辑:在定时器触发的信号处理函数中,检查是否有未确认的数据,并进行重传。例如:
Packet packets[10];
int packet_count = 0;

void timeout_handler(int signum) {
    for (int i = 0; i < packet_count; i++) {
        struct timeval now;
        gettimeofday(&now, NULL);
        long elapsed_time = (now.tv_sec - packets[i].send_time.tv_sec) * 1000000 + (now.tv_usec - packets[i].send_time.tv_usec);
        if (elapsed_time > 1000000) {  // 假设1秒未确认则重传
            // 重传packets[i].data
            printf("Retransmitting packet: %s\n", packets[i].data);
        }
    }
}

2. 处理数据包顺序

  1. 给数据包编号:在发送端,为每个数据包分配一个唯一的编号。可以在数据包结构体中添加一个编号字段:
typedef struct {
    int seq_num;
    char data[1024];
    struct timeval send_time;
} Packet;

发送时递增编号:

int seq_num = 0;
Packet packet;
packet.seq_num = seq_num++;
// 填充数据并发送
  1. 接收端处理:接收端维护一个缓冲区,根据数据包编号将数据包按顺序排列。例如:
#define MAX_SEQ 100
Packet received_packets[MAX_SEQ];
int received_flags[MAX_SEQ] = {0};

void process_packet(Packet packet) {
    if (packet.seq_num >= 0 && packet.seq_num < MAX_SEQ) {
        received_packets[packet.seq_num] = packet;
        received_flags[packet.seq_num] = 1;
        // 检查是否有连续的数据包可以处理
        for (int i = 0; i < MAX_SEQ; i++) {
            if (!received_flags[i]) {
                break;
            }
            // 处理received_packets[i].data
            printf("Processing packet: %s\n", received_packets[i].data);
            received_flags[i] = 0;
            // 移动后续数据包
            for (int j = i + 1; j < MAX_SEQ; j++) {
                received_packets[j - 1] = received_packets[j];
                received_flags[j - 1] = received_flags[j];
            }
        }
    }
}

3. 确认机制

  1. 发送确认包:接收端接收到数据包后,向发送端发送确认包(ACK)。确认包可以只包含被确认数据包的编号。
  2. 发送端处理确认包:发送端接收到确认包后,标记对应的数据包已被确认,不再进行重传。例如,可以在发送端维护一个确认标志数组:
int ack_flags[MAX_SEQ] = {0};
// 接收到ACK包处理函数
void handle_ack(int seq_num) {
    if (seq_num >= 0 && seq_num < MAX_SEQ) {
        ack_flags[seq_num] = 1;
    }
}

在重传逻辑中,只重传未被确认的数据包:

void timeout_handler(int signum) {
    for (int i = 0; i < packet_count; i++) {
        if (!ack_flags[packets[i].seq_num]) {
            // 重传packets[i].data
            printf("Retransmitting packet: %s\n", packets[i].data);
        }
    }
}