успешный send после удалённого shutdown
От: 0x656b694d Россия  
Дата: 05.12.08 18:27
Оценка:
Привет,

не удалось найти ответа на достаточно банальный вопрос: как узнать, что TCP соединение было закрыто удалённым сервером?

Что происходит:
HTTP/1.1, persistent connection, открыт сокет, послан запрос, получен ответ, сидим ждём.
Сервер закрывает соединение по таймауту (15 секунд по умолчанию у Апача, FIN-ACK, FIN-ACK посланы/приняты), но на стороне клиента на уровне сокетов ничего не заметно: send по-прежнему может отправить запрос, но первый же recv возвращает EOF.

В таком случае мне необходимо открыть новый сокет и заново послать тот же запрос. Хотелось бы знать до отправки, что соединения уже нет.
Как быть? Может можно как-нибудь сделать flush буферов или что-нибудь?

Вот код, например (вывод значений res покоцал). Второй вызов dojob получает EOF на чтении из сокета. Никаких ошибок не возвращается.

#include <errno.h>
#include <iostream>
#include <signal.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>

using namespace std;

const char req[] =
"GET /media/cup.3gp HTTP/1.1\r\n"
"Host: 10.16.107.47:80\r\n"
"Content-Length: 0\r\n"
"\r\n";

void dojob(int s) {
        int res;
        res = send(s, req, sizeof(req)/sizeof(req[0]), 0);
        char buf[200];
        do {
                res = recv(s, buf, sizeof(buf)/sizeof(buf[0]), 0);
        } while (res > 0);
}

int main(int argc, const char** argv) {

        int s = socket(PF_INET, SOCK_STREAM, 0);
        int res = 1;
        struct sockaddr_in sa;
        memset(&sa, 0, sizeof(sa));
        sa.sin_family = AF_INET;
        sa.sin_port = htons(80);
        sa.sin_addr.s_addr = htonl(0x0A106B2F);
        res = connect(s, (const sockaddr*)&sa, sizeof(sa));
        dojob(s);
        sleep(20);
        dojob(s);
        close(s);
        return 0;
}


Пробовал выставлять TCP_NODELAY, и прочие вещи, безрезультатно.

Спасибо.
Re: успешный send после удалённого shutdown
От: Vamp Россия  
Дата: 05.12.08 20:08
Оценка: 2 (1)
В общем случае, никак.
Да здравствует мыло душистое и веревка пушистая.
Re: успешный send после удалённого shutdown
От: TarasCo  
Дата: 05.12.08 20:59
Оценка: 2 (1)
Все дело в том, что TCP — дуплексный протокол и в нем разрешены т.н полузакрытые соединения. Каждый хост может закрыть только СВОЮ половину ( т.е передающую ). Если сервер написан правильно, он вызовет shutdown( SD_SEND ) — это как раз закрытие своей половины соединения, т.е посылка FIN и прием на него ACK. Далее он должен ждать пока вы соизволите закрыть свою половину соединения. Если вы считаете, что такое поведение не корректно, вы должны контролировать закрытие удаленным хостом соединения. Делается это тривиально — зовется recv пока он не вернет 0.
Да пребудет с тобою сила
Re[2]: успешный send после удалённого shutdown
От: 0x656b694d Россия  
Дата: 08.12.08 12:37
Оценка:
Интересно, что пакеты FIN и ACK проходят с обоих концов. Т.е. со стороны клиента тоже кто-то выслал FIN и ACK.

Я считаю, что на TCP соединении метод записи в сокет должен возвращать ошибку после такого закрытия. Выходит, что в случае HTTP/1.1 я вынужден послать запрос, и если вместа ответа приходит EOF, то закрыть сокет, открыть новый, и повторить запрос. Не очень красиво выглядит, по-моему.

Спасибо.
Re[3]: успешный send после удалённого shutdown
От: TarasCo  
Дата: 08.12.08 12:55
Оценка:
Здравствуйте, 0x656b694d, Вы писали:

0>Интересно, что пакеты FIN и ACK проходят с обоих концов. Т.е. со стороны клиента тоже кто-то выслал FIN и ACK.

Значит, вы где то в коде позвали shutdown ( SD_SEND или SD_BOTH ) или вызвали closesocket ( он по умолчанию также попытается закрыть корректно соединение ). Сами FIN ACK и не могут посылаться.

0>Я считаю, что на TCP соединении метод записи в сокет должен возвращать ошибку после такого закрытия. Выходит, что в случае HTTP/1.1 я вынужден послать запрос, и если вместа ответа приходит EOF, то закрыть сокет, открыть новый, и повторить запрос. Не очень красиво выглядит, по-моему.


Если вы закроете канал для записи, при попытке записать что то после — получите ошибку, вроде ESHUTDOWN . Т.е алогритм простой — всегда нужно опрашивать приемный конец, даже если вы не ждете данных — может придти FIN и на него надо как о отреагировать.
Да пребудет с тобою сила
Re[4]: успешный send после удалённого shutdown
От: 0x656b694d Россия  
Дата: 08.12.08 14:49
Оценка:
Здравствуйте, TarasCo, Вы писали:

0>>Интересно, что пакеты FIN и ACK проходят с обоих концов. Т.е. со стороны клиента тоже кто-то выслал FIN и ACK.

TC>Значит, вы где то в коде позвали shutdown ( SD_SEND или SD_BOTH ) или вызвали closesocket ( он по умолчанию также попытается закрыть корректно соединение ). Сами FIN ACK и не могут посылаться.
Я играюсь с кодом, приведённым в первом сообщении. Там я нигде не закрываю сокет между запросами.
Я не знаю, могут ли промежуточные шлюзы подтверждать закрытие вместо меня. Если так, то 1) это странно, и 2) придётся выкручиваться.

TC>Если вы закроете канал для записи, при попытке записать что то после — получите ошибку, вроде ESHUTDOWN . Т.е алогритм простой — всегда нужно опрашивать приемный конец, даже если вы не ждете данных — может придти FIN и на него надо как о отреагировать.

Если бы было не так, я бы был ещё больше недоволен. Но ведь я сам не закрываю ничего.
Re[5]: успешный send после удалённого shutdown
От: TarasCo  
Дата: 08.12.08 21:06
Оценка: 2 (1)
0>Я не знаю, могут ли промежуточные шлюзы подтверждать закрытие вместо меня. Если так, то 1) это странно, и 2) придётся выкручиваться.

А вы наблюдаете на сервере ответ FIN-ACK от клиента, хотя клиент соединение не закрывал ( по вашим словам )? Это может быть если ваш клиент соединяется через прокси сервер, поскольку он переустанавливает соединение.
Да пребудет с тобою сила
Re[6]: успешный send после удалённого shutdown
От: 0x656b694d Россия  
Дата: 09.12.08 17:21
Оценка:
Здравствуйте, TarasCo, Вы писали:
TC>А вы наблюдаете на сервере ответ FIN-ACK от клиента, хотя клиент соединение не закрывал ( по вашим словам )? Это может быть если ваш клиент соединяется через прокси сервер, поскольку он переустанавливает соединение.

Да, это меня и смутило — FIN-ACK с обоих концов. Скорее всего виноваты промежуточные вещи, которых есть некоторое количество.

Спасибо, теперь буду готовым и к таким поворотам, хотя выглядит по-прежнему странновато.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.