2012-04-17 264 views
8

我需要使用epoll for Linux的tcp客户端的异步连接和断开连接。有分机。在Windows中的功能,如ConnectEx,DisconnectEx,AcceptEx等... 在tcp服务器标准接受函数正在工作,但在tcp客户端不工作连接和断开...所有套接字都是非阻塞的。异步连接和断开与epoll(Linux)

我该怎么做?

谢谢!

+0

这可以帮助你: http://stackoverflow.com/questions/2875002/non-blocking-tcp-connect-with-epoll – 2012-04-17 08:11:47

+0

作为链接DJB页面建议的可能替代方案,我想建议尝试“dup '和'关闭'描述符(并使用重复)。没有测试,但它应该工作,在我的理解。文档声明,不检查'close'的返回值是严重的编程错误,因为它可能会返回以前的错误。这正是你想要的(如果'close'给出错误,'connect'失败)。虽然当然如果你使用'epoll',那么你肯定会有一个操作系统,其中'getsockopt(SO_ERROR)'将会正常工作... – Damon 2012-04-17 09:08:03

+1

如果可行,最简单的选择是等到connect()返回之后再设置NON_BLOCK。 – delicateLatticeworkFever 2012-04-17 13:11:10

回答

29

要做到无阻塞连接(),假设插座已经作出无阻塞:

int res = connect(fd, ...); 
if (res < 0 && errno != EINPROGRESS) { 
    // error, fail somehow, close socket 
    return; 
} 

if (res == 0) { 
    // connection has succeeded immediately 
} else { 
    // connection attempt is in progress 
} 

对于第二种情况,其中连接()与EINPROGRESS失败(只有在这种情况下, ),你必须等待套接字是可写的,例如对于epoll,请指定您正在等待此套接字上的EPOLLOUT。一旦你收到此通知,它是可写的(有epoll的,希望得到一个EPOLLERR或EPOLLHUP事件),检查连接尝试的结果:

int result; 
socklen_t result_len = sizeof(result); 
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) { 
    // error, fail somehow, close socket 
    return; 
} 

if (result != 0) { 
    // connection failed; error code is in 'result' 
    return; 
} 

// socket is ready for read()/write() 

根据我的经验,在Linux上,连接()从不立即成功,你总是需要等待可写性。然而,例如,在FreeBSD上,我发现无阻塞connect()到localhost是成功的。

+0

这是行不通的! – Matt 2012-10-25 04:04:40

+0

@Matt我知道这样做,你可能在这里做错了什么。你到底在想什么,它在哪里失败?你使用fcntl将套接字置于非阻塞模式吗? – 2012-10-25 11:51:22

+0

糟糕,没关系。我的一个错误。是的,我有非阻塞的插座。检查连接的结果时,我有一个错误!经典的C错误。 – Matt 2012-10-25 22:09:18

0

我有一个“完整”的答案在这里以防别人正在寻找这样的:

#include <sys/epoll.h> 
#include <errno.h> 
.... 
.... 
int retVal = -1; 
socklen_t retValLen = sizeof (retVal); 

int status = connect(socketFD, ...); 
if (status == 0) 
{ 
    // OK -- socket is ready for IO 
} 
else if (errno == EINPROGRESS) 
{ 
    struct epoll_event newPeerConnectionEvent; 
    int epollFD = -1; 
    struct epoll_event processableEvents; 
    unsigned int numEvents = -1; 

    if ((epollFD = epoll_create (1)) == -1) 
    { 
     printf ("Could not create the epoll FD list. Aborting!"); 
     exit (2); 
    }  

    newPeerConnectionEvent.data.fd = socketFD; 
    newPeerConnectionEvent.events = EPOLLOUT | EPOLLIN | EPOLLERR; 

    if (epoll_ctl (epollFD, EPOLL_CTL_ADD, socketFD, &newPeerConnectionEvent) == -1) 
    { 
     printf ("Could not add the socket FD to the epoll FD list. Aborting!"); 
     exit (2); 
    } 

    numEvents = epoll_wait (epollFD, &processableEvents, 1, -1); 

    if (numEvents < 0) 
    { 
     printf ("Serious error in epoll setup: epoll_wait() returned < 0 status!"); 
     exit (2); 
    } 

    if (getsockopt (socketFD, SOL_SOCKET, SO_ERROR, &retVal, &retValLen) < 0) 
    { 
     // ERROR, fail somehow, close socket 
    } 

    if (retVal != 0) 
    { 
     // ERROR: connect did not "go through" 
    } 
} 
else 
{ 
    // ERROR: connect did not "go through" for other non-recoverable reasons. 
    switch (errno) 
    { 
    ... 
    } 
} 
+0

我相信你的错误检查epoll_wait()是不正确的 - 你应该总是通过getsockopt检查连接尝试的结果( SO_ERROR),即使你没有得到EPOLLERR。在手册页http://linux.die.net/man/2/connect中看到EINPROGRESS另外,assert()是处理严重错误的错误方法 - 这意味着你已经*证明*它永远不会发生。使用exit(),即使定义了NDEBUG,也会终止程序。 – 2012-06-05 21:47:47

+0

刚刚添加了建议的编辑。未编辑的版本似乎适用于我。 – Sonny 2012-06-06 01:20:11

2

从经验来看,检测无阻塞连接时,epoll的是选择和投票方式有点不同。

with epoll:

connect()调用完成后,检查返回码。

如果连接无法立即完成,请用epoll注册EPOLLOUT事件。

调用epoll_wait()。

如果连接失败,您的事件将被填充EPOLLERR或EPOLLHUP,否则EPOLLOUT将被触发。

+0

是的,我在回答中忘记提及epoll可以返回EPOLLERR或EPOLLHUP以及EPOLLOUT。感谢提及,它已被纠正。 – 2013-10-08 12:08:36

1

我已经尝试了桑尼的解决方案,epoll_ctl将返回无效的参数。所以我想也许这样做是按照正确的方式:

1.创建socketfd和epollfd

2.使用epoll_ctl到socketfd和epollfd与epoll的事件相关联。

3.do连接(socketfd,...)

4.检查返回值或errno

5.如果错误号== EINPROGRESS,做epoll_wait