使用 epoll 进行异步连接和断开连接 (Linux)

问题描述 投票:0回答:4

我需要在 Linux 上使用 epoll 为 TCP 客户端进行异步连接和断开连接。有分机。 Windows 中的函数,如 ConnectEx、DisconnectEx、AcceptEx 等 对于TCP服务器来说,标准的accept函数正在工作。但是,TCP 客户端无法正确连接和断开连接。所有套接字都是非阻塞的。我怎样才能让它发挥作用?

linux sockets asynchronous tcp epoll
4个回答
40
投票

要执行非阻塞 connect(),假设套接字已设为非阻塞:

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
}

对于第二种情况,connect() 因 EINPROGRESS 失败(并且仅在这种情况下),您必须等待套接字可写,例如对于 epoll 指定您正在等待此套接字上的 EPOLLOUT。一旦您收到可写的通知(使用 epoll,also 希望获得 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 上,connect() 永远不会立即成功,你总是必须等待可写性。然而,例如,在 FreeBSD 上,我看到非阻塞 connect() 到 localhost 立即成功。


3
投票

从经验来看,在检测非阻塞连接时,epoll 与 select 和 poll 有点不同。

使用 epoll:

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

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

调用epoll_wait()。

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


1
投票

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

#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)
   {
     ...
   }
}

1
投票

我已经尝试了Sonny的解决方案,epoll_ctl将返回无效的参数。所以我认为也许正确的方法如下:

1.创建socketfd和epollfd

2.使用epoll_ctl将socketfd和epollfd与epoll事件关联起来。

3.do connect(socketfd,...)

4.检查返回值或errno

5.如果 errno == EINPROGRESS,执行 epoll_wait

© www.soinside.com 2019 - 2024. All rights reserved.