MST

星途 面试题库

面试题:C语言Linux TCP客户端优化问题

在已实现的Linux C语言TCP客户端基础上,如何处理网络拥塞、连接超时和重连机制,从代码设计和实现角度详细阐述。
18.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

处理网络拥塞

  1. 代码设计
    • 使用拥塞控制算法:在TCP协议栈中,Linux已经实现了多种拥塞控制算法,如CUBIC、reno等。应用层可以通过setsockopt函数设置TCP拥塞控制算法。
    • 调整发送窗口:TCP拥塞控制的核心是动态调整发送窗口大小。发送方根据网络反馈(如ACK包的到达情况)来判断网络是否拥塞,进而调整发送窗口。
  2. 代码实现
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    
    int main() {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        // 设置TCP拥塞控制算法为CUBIC
        const char* cubit = "cubic";
        if (setsockopt(sockfd, IPPROTO_TCP, TCP_CONGESTION, cubit, strlen(cubit)) < 0) {
            perror("setsockopt for TCP_CONGESTION failed");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
    
        // 后续连接服务器等代码...
    
        close(sockfd);
        return 0;
    }
    

处理连接超时

  1. 代码设计
    • 设置socket选项:使用setsockopt函数设置SO_SNDTIMEO和SO_RCVTIMEO选项,分别控制发送和接收操作的超时时间。
    • 使用select或epoll:在等待连接建立时,可以使用select或epoll机制来设置等待的超时时间。
  2. 代码实现
    • 使用setsockopt设置超时
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <time.h>
    
    int main() {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        struct timeval timeout;
        timeout.tv_sec = 5; // 设置5秒超时
        timeout.tv_usec = 0;
    
        if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)) < 0) {
            perror("setsockopt for SO_SNDTIMEO failed");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
    
        if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)) < 0) {
            perror("setsockopt for SO_RCVTIMEO failed");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(8080);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
            perror("connect failed");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
    
        // 后续通信代码...
    
        close(sockfd);
        return 0;
    }
    
    • 使用select设置连接超时
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <sys/select.h>
    
    int main() {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(8080);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(sockfd, &fds);
    
        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
    
        int ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        if (ret < 0 && errno == EINPROGRESS) {
            ret = select(sockfd + 1, NULL, &fds, NULL, &timeout);
            if (ret == 1) {
                int so_error;
                socklen_t len = sizeof(so_error);
                if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) == 0 && so_error == 0) {
                    // 连接成功
                    // 后续通信代码...
                } else {
                    perror("connect error after select");
                }
            } else {
                perror("select timeout");
            }
        } else if (ret < 0) {
            perror("connect failed");
        }
    
        close(sockfd);
        return 0;
    }
    

处理重连机制

  1. 代码设计
    • 记录连接状态:在客户端程序中,需要一个变量来记录当前的连接状态,如是否连接成功。
    • 设置重连策略:可以设置固定时间间隔重连,也可以采用指数退避策略,即每次重连的时间间隔逐渐增大。
  2. 代码实现
    • 固定时间间隔重连
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <time.h>
    
    int main() {
        int sockfd;
        int connected = 0;
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(8080);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        while (!connected) {
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (sockfd < 0) {
                perror("socket creation failed");
                sleep(5); // 等待5秒后重连
                continue;
            }
    
            if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) {
                connected = 1;
            } else {
                perror("connect failed");
                close(sockfd);
                sleep(5); // 等待5秒后重连
            }
        }
    
        // 连接成功后的通信代码...
    
        close(sockfd);
        return 0;
    }
    
    • 指数退避重连
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <time.h>
    
    int main() {
        int sockfd;
        int connected = 0;
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(8080);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        int retry_time = 1;
        while (!connected) {
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (sockfd < 0) {
                perror("socket creation failed");
                sleep(retry_time);
                retry_time *= 2;
                continue;
            }
    
            if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) {
                connected = 1;
            } else {
                perror("connect failed");
                close(sockfd);
                sleep(retry_time);
                retry_time *= 2;
            }
        }
    
        // 连接成功后的通信代码...
    
        close(sockfd);
        return 0;
    }