MST

星途 面试题库

面试题:C语言UDP高并发编程下的可靠性保障策略

在Linux系统中,当使用C语言进行UDP高并发编程时,为确保数据传输的可靠性,会面临哪些挑战?针对这些挑战,阐述你所了解的解决方案以及如何在代码层面进行优化,以减少丢包并保证数据的有序到达。
36.6万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

面临的挑战

  1. 网络延迟和丢包:网络拥塞、信号干扰等因素可能导致数据包丢失或延迟到达。
  2. 无序到达:UDP 是无连接协议,数据包可能因路由等原因无序到达。
  3. 高并发处理:大量并发连接时,操作系统资源(如文件描述符、内存等)管理困难,且可能出现处理性能瓶颈。

解决方案

  1. 网络延迟和丢包
    • 重传机制:发送端记录已发送数据包,设置定时器,若超时未收到确认,重发数据包。
    • 前向纠错(FEC):在发送数据中加入冗余信息,接收端利用冗余恢复丢失数据包。
  2. 无序到达
    • 序列号:给每个数据包添加序列号,接收端按序列号排序重组数据。
  3. 高并发处理
    • 高效的 I/O 模型:如使用 epoll(Linux 下)进行多路复用,提高 I/O 处理效率。
    • 线程池或进程池:合理分配任务,避免频繁创建和销毁线程或进程带来的开销。

代码层面优化

  1. 重传机制实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8080
#define BUFFER_SIZE 1024
#define SERVER_IP "127.0.0.1"
#define TIMEOUT_SECONDS 2

int main() {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    memset(&cliaddr, 0, sizeof(cliaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);

    char buffer[BUFFER_SIZE];
    int n, len;
    // 设置定时器
    struct timeval timeout;
    timeout.tv_sec = TIMEOUT_SECONDS;
    timeout.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

    while (1) {
        printf("Enter message to send: ");
        fgets(buffer, BUFFER_SIZE, stdin);
        buffer[strcspn(buffer, "\n")] = '\0';
        int sent = 0;
        while (!sent) {
            sendto(sockfd, (const char *)buffer, strlen(buffer), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
            n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
            buffer[n] = '\0';
            if (strcmp(buffer, "ACK") == 0) {
                sent = 1;
            }
        }
    }
    close(sockfd);
    return 0;
}
  1. 序列号实现
// 假设结构体包含序列号和数据
typedef struct {
    int seq_num;
    char data[BUFFER_SIZE];
} Packet;

// 发送端添加序列号
Packet send_packet;
send_packet.seq_num = sequence_number++;
strcpy(send_packet.data, "example data");
sendto(sockfd, (const char *)&send_packet, sizeof(Packet), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));

// 接收端按序列号处理
Packet received_packet;
recvfrom(sockfd, (char *)&received_packet, sizeof(Packet), MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
// 存储到合适的数据结构并按序列号排序
  1. 使用 epoll 优化 I/O
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>

#define PORT 8080
#define MAX_EVENTS 10
#define BUFFER_SIZE 1024

int main() {
    int sockfd, epollfd;
    struct sockaddr_in servaddr;
    struct epoll_event ev, events[MAX_EVENTS];

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    epollfd = epoll_create1(0);
    if (epollfd == -1) {
        perror("epoll_create1");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
        perror("epoll_ctl: add");
        close(sockfd);
        close(epollfd);
        exit(EXIT_FAILURE);
    }

    while (1) {
        int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        for (int i = 0; i < nfds; ++i) {
            if (events[i].data.fd == sockfd) {
                char buffer[BUFFER_SIZE];
                struct sockaddr_in cliaddr;
                socklen_t len = sizeof(cliaddr);
                int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *) &cliaddr, &len);
                buffer[n] = '\0';
                printf("Received: %s\n", buffer);
            }
        }
    }
    close(sockfd);
    close(epollfd);
    return 0;
}