注册

Linux报 “transport endpoint is already connected” 异常的原因以及解决办法

在Linux中,当你通过一些网络协议(如TCP、UDP、IPC)建立连接时,这些连接被称作“transport endpoint”(传输端点)。当出现“transport endpoint is already connected”这个错误时,意味着连接已经存在,且正在尝试重新连接,导致错误。下面我将详细讲解此问题的原因和解决方法。

原因:

这个错误的原因是尝试重新连接一个已经建立的连接。当你已经建立了一个连接并想再次连接同一个外部资源时(如同一IP地址),就会出现这个错误。

解决方法:

方法一:使用SO_REUSEADDR选项

SO_REUSEADDR是Linux套接字API中socket选项之一,它可以让以前使用的端口立即变得可用。这个选项可以在socket()函数之后,bind()函数之前设置。

int optval = 1;
setsockopt(socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

下面是一个示例:

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

#define PORT 8080 
#define MAXLINE 1024 

int main() { 
    int sockfd; 
    char buffer[MAXLINE]; 
    char *hello = "Hello from server"; 
    struct sockaddr_in servaddr, cliaddr; 

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

    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

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

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

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

    int len, n; 
    len = sizeof(cliaddr);  

    n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, ( struct sockaddr *) &cliaddr, &len); 
    buffer[n] = '\0'; 
    printf("Client : %s\n", buffer); 
    sendto(sockfd, (const char *)hello, strlen(hello),  MSG_CONFIRM, (const struct sockaddr *) &cliaddr, sizeof(cliaddr)); 
    printf("Hello message sent.\n");  

    return 0; 
} 

方法二:等待一段时间后再连接

如果SO_REUSEADDR选项无法解决问题,你也可以使用“退避重试”策略,即等待一段时间后再尝试连接。

下面是一个示例:

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

#define PORT 8080 
#define MAXLINE 1024 

int main() { 
    int sockfd; 
    char buffer[MAXLINE]; 
    char *hello = "Hello from server"; 
    struct sockaddr_in servaddr, cliaddr; 

    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 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_addr.s_addr = INADDR_ANY; 
    servaddr.sin_port = htons(PORT); 

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

    int len, n; 
    len = sizeof(cliaddr);  

    n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, ( struct sockaddr *) &cliaddr, &len); 

    if (n < 0) {
        // 错误处理
    } else if (n == 0) {
        // 连接已断开
    } else {
        buffer[n] = '\0'; 
        printf("Client : %s\n", buffer); 
        sendto(sockfd, (const char *)hello, strlen(hello),  MSG_CONFIRM, (const struct sockaddr *) &cliaddr, sizeof(cliaddr)); 
        printf("Hello message sent.\n");  
    }

    close(sockfd);

    sleep(5); // 等待 5 秒

    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 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_addr.s_addr = INADDR_ANY; 
    servaddr.sin_port = htons(PORT); 

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

    len = sizeof(cliaddr);  

    n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, ( struct sockaddr *) &cliaddr, &len); 

    if (n < 0) {
        // 错误处理
    } else if (n == 0) {
        // 连接已断开
    } else {
        buffer[n] = '\0'; 
        printf("Client : %s\n", buffer); 
        sendto(sockfd, (const char *)hello, strlen(hello),  MSG_CONFIRM, (const struct sockaddr *) &cliaddr, sizeof(cliaddr)); 
        printf("Hello message sent.\n");  
    }

    close(sockfd);  

    return 0; 
} 

在这个示例中,我们首先通过socket()和bind()函数建立了一个服务器socket,然后通过recvfrom()函数从客户端读取数据。如果recvfrom()函数返回“transport endpoint is already connected”错误,我们就等待5秒后再尝试重新连接。重新连接的过程与之前的过程相同,只是SO_REUSEADDR选项不再设置。