C套接字编程:connect()挂起

4

大家好,我快被搞疯了。我有一个客户端尝试连接服务器,看起来一切正常,使用gethostbyname()socket()bind(),但当尝试connect()时,它就会一直停在那里,而服务器没有从客户端接收到任何信息。我知道服务器是可以工作的,因为另一个(也是C语言编写的)客户端可以顺利连接。是什么导致了服务器无法看到这个传入的连接?我已经束手无策了。这两个不同的客户端非常相似,所以我更加困惑了。

    if (argc == 2)  {
    host = argv[1];         // server address
}
else    {
    printf("plz read the manual\n");
    exit(1);
}

hserver = gethostbyname(host);
if (hserver)    {
    printf("host found: %p\n", hserver);
    printf("host found: %s\n", hserver->h_name );
}
else    {
    printf("host not found\n");
    exit(1);
}

bzero((char * ) &server_address, sizeof(server_address)); // copy zeroes into string
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(hserver->h_addr);
server_address.sin_port = htons(SERVER_PORT);

bzero((char * ) &client_address, sizeof(client_address)); // copy zeroes into string
client_address.sin_family = AF_INET;
client_address.sin_addr.s_addr = htonl(INADDR_ANY);
client_address.sin_port = htons(SERVER_PORT);

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
    exit(1);
else    {
    printf("socket is opened: %i \n", sockfd);
    info.sock_fd = sockfd;
    rv = fcntl(sockfd, F_SETFL, O_NONBLOCK); // socket set to NONBLOCK
    if(rv < 0)
        printf("nonblock failed: %i %s\n", errno, strerror(errno));
    else
        printf("socket is set nonblock\n");
}

timeout.tv_sec = 0;     // seconds
timeout.tv_usec = 500000; // micro seconds ( 0.5 seconds)
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval));

rv = bind(sockfd, (struct sockaddr *) &client_address, sizeof(client_address));
if (rv < 0)     {
    printf("MAIN: ERROR bind() %i: %s\n", errno, strerror(errno));
    exit(1);
}
else
    printf("socket is bound\n");

rv = connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address));
printf("rv = %i\n", rv);
if (rv < 0)     {
    printf("MAIN: ERROR connect() %i:  %s\n", errno, strerror(errno));
    exit(1);
}
else
    printf("connected\n");

非常感谢您的想法和见解。
- Fourier 编辑: 如果套接字没有设置为非阻塞,则会挂起。 如果套接字设置为非阻塞,则会收到“ERROR connect() 115: Operation now in progress”错误。
[EINPROGRESS] 对于套接字的文件描述符设置了O_NONBLOCK,并且不能立即建立连接;连接将异步建立。
我还想提到的是,服务器和客户端运行在彼此旁边的计算机上,由一个路由器连接。

你能给我们提供errno的值吗? - ereOn
错误代码是115,但现在已经解决了。非常感谢。 - Fantastic Fourier
愚蠢的想法:服务器是否允许多个客户端连接?在“accept()”之后,它会生成一个线程、子进程或使用select()来重新启动接受传入连接并处理刚刚连接的客户端的流量吗? - emvee
Haavee:是的,服务器允许多个客户端连接。我有一个线程专门使用select()来检查传入的连接。 - Fantastic Fourier
4个回答

5

gethostbyname()函数生成网络字节序的地址,所以不需要通过htonl()传递。另外,hostent->h_addr条目是指向地址的指针。请将此行替换为:

server_address.sin_addr.s_addr = htonl(hserver->h_addr);

使用:

memcpy(&server_address.sin_addr, hserver->h_addr, hserver->h_length);

我还删除了非阻塞,现在它不再挂起,但是会出现111:连接被拒绝的错误。这很奇怪,因为我在同一台机器上运行另一个客户端(作为损坏的客户端),而服务器可以正常接受连接。 - Fantastic Fourier
“连接被拒绝”这个错误信息很明显。也许你的 SERVER_PORT 也设置错了。 - caf
是的,我简直不敢相信我没有发现那个。由于我的视力问题,12345和12346看起来非常相似。 - Fantastic Fourier

1

我看到你将socket设置为O_NONBLOCK模式。

因此,根据connect的手册页面,connect必须返回-1并设置errno为EAGAIN。

然后,您可以使用select()在socket上知道连接何时成功。

这是一种非常常见的模式,用于控制连接超时(因为select()必须带有超时)。


0

如果您想从同一台机器连接两次,我可以看到您的问题所在。

您正在绑定客户端套接字。您的代码非常具体地将客户端套接字绑定到固定端口(服务器端口)。这使得操作系统无法自由选择可用于建立连接的端口。如果一个进程已经分配了该端口(它已成功地绑定()和连接()到服务器),那么另一个进程就不能使用同一端口。

如果没有必要从特定端口发送流量,请通过更改此行让操作系统找到一个可用端口:

client_address.sin_port = htons(SERVER_PORT);

client_address.sin_port = 0;

这是很好的建议,但我认为这不是问题所在,因为bind()会失败(而且OP对该调用进行了错误检查)。 - caf
获取SO_REUSEADDR的值会很有趣。如果它设置为true(“1”),那么bind()将不会失败。 - emvee

0

检查您是否可以连接到程序telnet(它接受服务器名称和端口号)。如果可以连接,则错误必须在您的代码中。如果telnet也挂起,则请检查您的防火墙设置。


据我所知,他有两个客户端,其中一个已经成功连接。 - ereOn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接