我们遇到了一种情况,应用程序显然通过系统分配的端口号“半连接”到自身,并在 Linux 上“卡住”(尤其是 Centos 6.4)。
情况如下:
(python)应用程序正在尝试连接到某些服务,假设 IP 地址为 192.168.1.201:8081。由于某种原因,分配的传出端口是 8081。连接成功,但套接字上没有发生进一步的活动,因为它没有真正连接,而是半路连接(我的猜测是只完成了一半的握手,Linux 正在做其余的在后台以提高并行性)。套接字上的读取语句挂起,等待连接的其余部分完成。
这是一个复制该问题的简单 C++ 程序。使用您正在运行的主机的 IP 地址运行它。它很奇怪,因为我们将连接套接字绑定到一个端口(这是合法的),然后连接到相同的地址和端口,而不会生成错误消息,没有“监听”,并且读取挂起。
./foo 192.168.1.201 已连接...要阅读...'
ss-na ESTAB 0 0 192.168.1.201:8081 192.168.1.201:8081
如果终止程序,套接字将进入时间等待状态:
时间等待 0 0 192.168.1.201:8081 192.168.1.201:8081
问题是:系统分配的端口会发生这种情况吗?您是否会进入出站地址/端口最终与目标地址/端口匹配且系统死锁的状态?这似乎就是我们所看到的。
谢谢,
-- 迈克
节目:
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int ret;
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
struct sockaddr_in sa_loc;
if(argc != 2)
{
printf("\n Usage: %s <ip of server> \n",argv[0]);
return 1;
}
memset(recvBuff, '0',sizeof(recvBuff));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Error : Could not create socket \n");
return 1;
}
memset(&sa_loc, 0, sizeof(struct sockaddr_in));
sa_loc.sin_family = AF_INET;
sa_loc.sin_port = htons(8081);
sa_loc.sin_addr.s_addr = inet_addr(argv[1]);
ret = bind(sockfd, (struct sockaddr *)&sa_loc, sizeof(struct sockaddr));
if (ret != 0) {
perror("bind");
exit(1);
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8081);
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("\n inet_pton error occured\n");
return 1;
}
ret = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (ret != 0) {
perror("connect");
exit(1);
}
printf("Connected...going to read...\n"); fflush(stdout);
while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) {
recvBuff[n] = 0;
printf("%s", recvBuff); fflush(stdout);
}
if(n <= 0) {
perror("read");
exit(1);
}
return 0;
}
通过将套接字绑定到端口,然后连接到同一个端口,您就建立了套接字到自身的一种环回连接。
系统分配的端口会发生这种情况吗?
不,不能,因为 connect(3) 承诺:
如果套接字尚未绑定到本地地址, connect() 应将其绑定到一个地址,除非 socket的地址族是AF_UNIX,是一个未使用的本地地址。
这个未使用的本地地址永远不会与正在运行的服务器套接字绑定的地址相同。另外,如果该端口的服务器已经运行,则客户端中的
bind()
将不会成功。
您是否会进入出站地址/端口以某种方式结束的状态 up 匹配目标地址/端口并且系统死锁?
只有我们明确地以这种方式编程,通过绑定到我们随后连接的同一端口,我们才能进入观察状态。此外,系统不会死锁,只有应用程序会阻塞,很简单,因为它调用
read()
,等待从未写入的数据。