我目前正在尝试为打开tcp连接的方法添加超时。我大致使用了here找到的指南,只是我尝试使用poll()
而不是选择。但是,我对poll()
的调用会立即返回,通知fd已准备好写入,但连接未打开。这是我制作的简化代码示例:
#include <cstring>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <iostream>
#include <cstdlib>
int main() {
struct addrinfo hints;
::memset( &hints, 0, sizeof( hints ) );
hints.ai_socktype = 0;
hints.ai_family = AF_UNSPEC;
hints.ai_protocol = 0;
hints.ai_flags = AI_CANONNAME;
struct addrinfo * info;
if( ::getaddrinfo( "127.0.0.1", "49999", &hints, &info ) != 0 ) {
std::cerr << "Error: getaddrinfo" << std::endl;
exit( 1 );
}
int soc;
if( (soc = ::socket( info->ai_family, info->ai_socktype, info->ai_protocol ) ) < 0 ) {
std::cerr << "Erorr: socket" << std::endl;
}
// Set mode to non-blocking for timeout handling
int arg;
if( (arg = ::fcntl( soc, F_GETFL, 0 )) < 0 ) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
arg |= O_NONBLOCK;
if( ::fcntl(soc, F_SETFL, arg) < 0) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
int res = ::connect(soc,info->ai_addr,info->ai_addrlen);
if( (res != 0) && (errno != EINPROGRESS) ) {
std::cerr << "Error: connect" << std::endl;
exit( 1 );
}
pollfd pfd;
pfd.fd = soc;
pfd.events = POLLOUT;
res = ::poll( &pfd, 1, 50000 );
if( res < 0 ) {
std::cerr << "Error: poll" << std::endl;
exit( 1 );
}
else if( res == 0 ) {
std::cerr << "Error: poll" << std::endl;
exit( 1 );
}
// Set blocking mode again
if( (arg = ::fcntl(soc, F_GETFL, NULL)) < 0) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
arg &= (~O_NONBLOCK);
if( ::fcntl(soc, F_SETFL, arg) < 0) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
return 0;
}
由于端口49999
在我的机器上关闭,我预计会出错。相反,程序的返回值为0
。
我还尝试使用上面链接中的选择示例。如果我使用完整示例替换对poll()
的调用,则会收到以下错误消息:
Operation now in progress: Operation now in progres
我尝试使用select减少代码,但是当我减少代码时,我得到了正确的connection refused message
。
修改:
注意:“正在进行操作”消息是由于我身边的错误处理代码中的错误。一旦我纠正了这个,我从“getsockopt()”得到了正确的错误信息。这也解释了为什么我无法减少这个例子。
答案 0 :(得分:1)
如果连接失败,您只需要进行轮询。当连接到“localhost”时,它通常会立即成功。
答案 1 :(得分:0)
问题在于缺少getsockopt()
次来电。我假设如果poll()
没有成功,connect()
将返回错误,但只要状态可以确定就会立即通过。一旦我从示例中添加了getsockopt()
调用,它就可以正常工作。
以下是使用poll的一些(不太干净)示例代码:
#include <cstring>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <iostream>
#include <cstdlib>
#include <cstdio>
int main() {
struct addrinfo hints;
::memset( &hints, 0, sizeof( hints ) );
hints.ai_socktype = 0;
hints.ai_family = AF_UNSPEC;
hints.ai_protocol = 0;
hints.ai_flags = AI_CANONNAME;
struct addrinfo * info;
if( ::getaddrinfo( "127.0.0.1", "49999", &hints, &info ) != 0 ) {
std::cerr << "Error: getaddrinfo" << std::endl;
exit( 1 );
}
int soc;
if( (soc = ::socket( info->ai_family, info->ai_socktype, info->ai_protocol ) ) < 0 ) {
std::cerr << "Erorr: socket" << std::endl;
}
// Set mode to non-blocking for timeout handling
int arg;
if( (arg = ::fcntl( soc, F_GETFL, 0 )) < 0 ) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
arg |= O_NONBLOCK;
if( ::fcntl(soc, F_SETFL, arg) < 0) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
int res = ::connect(soc,info->ai_addr,info->ai_addrlen);
if( res < 0 ) {
if( errno == EINPROGRESS ) {
pollfd pfd;
pfd.fd = soc;
pfd.events = POLLOUT;
std::cout << "Polling" << std::endl;
res = ::poll( &pfd, 1, 50000 );
if( res < 0 ) {
std::cerr << "Error: poll" << std::endl;
exit( 1 );
}
else if( res == 0 ) {
std::cerr << "Error: poll" << std::endl;
exit( 1 );
} else {
socklen_t lon = sizeof(int);
int valopt;
if (getsockopt(soc, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno));
exit(0);
}
// Check the value returned...
if (valopt) {
fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt));
exit(0);
}
}
}
else {
std::cerr << "Error: connect" << std::endl;
exit( 1 );
}
}
// Set blocking mode again
if( (arg = ::fcntl(soc, F_GETFL, NULL)) < 0) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
arg &= (~O_NONBLOCK);
if( ::fcntl(soc, F_SETFL, arg) < 0) {
std::cerr << "Error: fcntl" << std::endl;
exit( 1 );
}
return 0;
}