Socket select () Обработка резких отключений - PullRequest
2 голосов
/ 30 апреля 2010

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

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

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

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

Ответы [ 3 ]

2 голосов
/ 01 января 2012

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

//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;
}
1 голос
/ 16 сентября 2012

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

catch(Poco::Net::ConnectionResetException &ex)
{
    _socket.shutdownSend();
    delete this;
}
1 голос
/ 03 мая 2010

Кажется, ты прав, Реми. Мне удалось определить, был ли сокет отключен, используя следующий код (он был добавлен в 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 в закрытом сокете.

...