Socket select() Обработка внезапных отключений

В настоящее время я пытаюсь исправить ошибку на прокси-сервере, которую я написал в связи с вызовом socket select(). Я использую библиотеки Poco C++ (используя SocketReactor), и проблема на самом деле в коде Poco, который может быть ошибкой, но я еще не получил от них подтверждения этого.

Что происходит, так это то, что всякий раз, когда соединение внезапно прерывается, вызов сокета select() немедленно возвращается, что, как я полагаю, он и должен делать? В любом случае, он возвращает все отключенные сокеты в доступном для чтения наборе файловых дескрипторов, но проблема в том, что исключение «Сокет не подключен» возникает, когда Poco пытается запустить обработчик события onReadable, в который я бы поместил код для справиться с этим. Учитывая, что исключение перехватывается молча, а событие onReadable никогда не запускается, вызов select() продолжает немедленно возвращаться, что приводит к бесконечному циклу в SocketReactor.

Я рассматривал возможность изменения кода Poco, чтобы вместо того, чтобы автоматически перехватывать исключение, он запускал новое событие с именем onDisconnected или что-то в этом роде, чтобы можно было выполнить очистку.

Мой вопрос: есть ли какие-нибудь элегантные способы определить, закрылся ли сокет ненормально, используя вызовы select()? Я думал об использовании сообщения об исключении, чтобы определить, когда это произошло, но мне это кажется грязным.


person Genesis    schedule 30.04.2010    source источник
comment
Вы зарегистрировали обработчики для события ErrorNotification?   -  person nos    schedule 30.04.2010
comment
Да, никакие события ошибок не запускаются.   -  person Genesis    schedule 30.04.2010
comment
Я ничего не знаю о Poco, но в целом select() не сообщает о каких-либо различиях между читаемым сокетом и отключенным сокетом. Он сообщает, что сокет доступен для чтения при закрытии/потере/завершении. Затем последующее чтение сокета указывает, произошло ли отключение и было ли оно корректным или ненормальным. Похоже, что Poco обнаруживает аномальное отключение при чтении сокета (отсюда и исключение), но вместо того, чтобы проглотить его и вернуться к select(), вместо этого он должен остановить обработку сокета. Это определенно указывает на ошибку внутри Poco для меня.   -  person Remy Lebeau    schedule 01.05.2010


Ответы (3)


У меня была такая же проблема. Единственный способ обойти это — контролировать код выхода клиентских приложений. Решение, которое я использовал, заключалось в том, чтобы отправить сигнал отключения до того, как реактор был остановлен на стороне клиента. Затем на сервере вы просто закрываете сокет.

//Client:
//Handler Class: onWrite
Packet p = Packet::Shutdown();

if (p.fn == "shutdown")
{
    _reactor.stop();
    delete this;
}

//Server
//Accepter Class: onRead
if (p.fn == "shutdown")
{
    printf("%s has disconnected", _username.c_str());
    _socket.close();
    delete this;
}
person kroz00    schedule 01.01.2012

Похоже, ты прав, Реми. Мне удалось определить, отключился ли сокет, используя следующий код (он был добавлен в Poco/Net/src/SocketImpl.cpp):

bool SocketImpl::isConnected()
{
int bytestoread;
int rc;
fd_set fdRead;

FD_ZERO(&fdRead);
FD_SET(_sockfd, &fdRead);

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 250000;

rc = ::select(int(_sockfd) + 1, &fdRead, (fd_set*) 0, (fd_set*) 0, &tv);
ioctl(FIONREAD, &bytestoread);

return !((bytestoread == 0) && (rc == 1));
}

Насколько я понимаю, это проверяет, доступен ли сокет для чтения, используя вызов select(), а затем проверяет фактическое количество байтов, доступных в этом сокете. Если сокет сообщает, что он доступен для чтения, но байты равны 0, то сокет на самом деле не подключен.

Хотя это отвечает на мой вопрос здесь, это, к сожалению, не решило мою проблему с Poco, поскольку я не могу найти способ исправить это в коде Poco SocketReactor. Я попытался создать новое событие под названием DisconnectNotification, но, к сожалению, не могу его вызвать, поскольку возникает та же ошибка, что и для ReadNotification в закрытом сокете.

person Genesis    schedule 03.05.2010

Просто поймайте ConnectionResetException в OnReadable() (обрабатывает ReadableNotification). Затем он правильно обрабатывает «Сброс соединения узлом».

catch(Poco::Net::ConnectionResetException &ex)
{
    _socket.shutdownSend();
    delete this;
}
person Cyber    schedule 16.09.2012