2016-03-27 71 views
0

如果之前有人问过这个问题,我很抱歉。我正在编写一个使用选择多路复用的非阻塞套接字客户端。让我困惑的一件事是,无论服务器在线还是离线,无阻塞连接都会成功。我搜索了很多帖子,并遵循他们的解决方案,但没有一个在我的linux ubuntu机器上工作。非阻塞套接字连接总是成功?

static void callback_on_select_write(int connect_fd) { 

    // Client write event arrived; 

    int error = -1; 
    socklen_t len = sizeof(error); 

    if(getsockopt(connect_fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) { 
    return; 
    } 

    // getsockopt puts the errno value for connect into erro so 0 means no-error. 
    if(error == 0) { 
     // Connection ok. 
    } 
    else { 
    cerr << "Failed to connect\n"; 
    return; 
    } 

    // Ready to write/read 

} 

每次选择返回并调用这个回调总是成功,即,将“准备读/写”块,而不是cerring故障。为什么会发生?如何设计便携式机制来检测连接是否真的成功?以下是我创建连接器的方式。

int make_socket_client(const ::std::string& hostname, const ::std::string& port) { 

    struct addrinfo hints; 
    struct addrinfo* res {nullptr}; 
    struct addrinfo* ptr {nullptr}; 

    memset(&hints, 0, sizeof(struct addrinfo)); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_protocol = IPPROTO_TCP; 

    int rv; 
    int connector; 

    if((rv = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res)) != 0) { 
    return -1; 
    } 

    // Try to get the first available client connection. 
    for(ptr = res; ptr != nullptr; ptr = ptr->ai_next) { 

    // Ignore undefined ip type. 
    if(ptr->ai_family != AF_INET && ptr->ai_family != AF_INET6) { 
     continue; 
    } 

    // Create a listener socket and bind it to the localhost as the server. 
    if((connector = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) == -1){ 
     continue; 
    } 

    make_fd_nonblocking(connector); 

    if(connect(connector, (struct sockaddr*)ptr->ai_addr, ptr->ai_addrlen) < 0) { 
     // This is what we expect. 
     if(errno == EINPROGRESS) { 
     break; 
     } 
     else { 
     close(connector); 
     continue; 
     } 
    } 
    else { 
     break; 
    } 
    } 

    freeaddrinfo(res); 

    if(ptr == nullptr) { 
    return -1; 
    } 

    return connector; 
} 
+0

当然,你应该看看'connect_fd'上的SO_ERROR?什么是'事件'?此代码不够完整,无法回答问题。 – EJP

+0

对不起。它只应该是connect_fd。 – Jes

+0

原始'connect()'返回零吗? – EJP

回答

0

每次选择返回并调用这个回调总是 成功,即,将“准备读/写”块,而不是 cerring故障。为什么会发生?

虽然异步TCP连接正在进行(如connect()调用中的-1/EINPROGRESS所示),但应将套接字传递给select()作为其准备写入套接字集的一部分,因此select()将在套接字指示它准备好写入时返回。

当TCP连接成功或失败时,select()将返回套接字准备好写入(*)。发生这种情况时,需要确定发生了两种可能的结果(成功或失败)中的哪一种。

以下是我在异步连接套接字select()准备好写入时调用的函数。

// call this select() has indicated that (fd) is ready-for-write because 
// (fd)'s asynchronous-TCP connection has either succeeded or failed. 
// Returns true if the connection succeeded, false if the connection failed. 
// If this returns true, you can then continue using (fd) as a normal 
// connected/non-blocking TCP socket. If this returns false, you should 
// close(fd) because the connection failed. 
bool FinalizeAsyncConnect(int fd) 
{ 
#if defined(__FreeBSD__) || defined(BSD) 
    // Special case for FreeBSD7, for which send() doesn't do the trick 
    struct sockaddr_in junk; 
    socklen_t length = sizeof(junk); 
    memset(&junk, 0, sizeof(junk)); 
    return (getpeername(fd, (struct sockaddr *)&junk, &length) == 0); 
#else 
    // For most platforms, the code below is all we need 
    char junk; 
    return (send(fd, &junk, 0, 0L) == 0); 
#endif 
} 

(*)附注:事情是Windows下略有不同,因为Windows喜欢做的事情自己的路:在Windows下,一个成功的异步连接()如上述表示,但如果你想在Windows下尝试失败的异步连接()尝试时,您需要在“except”fd_set下注册套接字,因为它将用于传递失败的异步连接()的“fd_set”除外。

+0

谢谢你,杰里米。但是,在我的OSX系统中,调用send与peer处于脱机状态会导致SIGPIPE错误,程序退出,函数FinalizeAsyncConnect永远不会返回。我怎样才能解决这个问题? – Jes

+1

通过调用main()顶部的信号(SIGPIPE,SIG_IGN)来禁用SIGPIPE,如下所述:http://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly/108192#108192 –