在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选项不再设置。