Async подключиться и отключиться с epoll (Linux)

Мне нужно асинхронное подключение и отключение для клиента TCP, используя epoll для Linux. Есть доп. функции в Windows, такие как ConnectEx, DisconnectEx, AcceptEx и т. д. В tcp server стандартная функция accept работает, но в tcp client не работает connect and connect... Все сокеты неблокирующиеся.

Как я могу это сделать?

Спасибо!

4 ответа

Чтобы сделать неблокирующую функцию 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, также ожидайте получения события 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, которая сразу же преуспела.

По опыту, при обнаружении неблокирующего соединения epoll немного отличается от select и poll.

с epoll:

После вызова connect() проверьте код возврата.

Если соединение не может быть установлено немедленно, зарегистрируйте событие EPOLLOUT в epoll.

Вызовите epoll_wait().

Если соединение не удалось, ваши события будут заполнены EPOLLERR или EPOLLHUP, в противном случае будет запущен EPOLLOUT.

Я попробовал решение Сонни, и epoll_ctl вернет неверный аргумент. Поэтому я думаю, что, возможно, правильный способ сделать это так:

1. создать socketfd и epollfd

2. Используйте epoll_ctl, чтобы связать socketfd и epollfd с событием epoll.

3. сделать подключение (socketfd,...)

4. проверить возвращаемое значение или errno

5. если errno == EINPROGRESS, сделайте epoll_wait

У меня есть "полный" ответ здесь на случай, если кто-то еще ищет это:

#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)
   {
     ...
   }
}
Другие вопросы по тегам