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