Как узнать, в каком случае неблокирующий recv возвращает 0?

У меня есть простой TCP-сервер, который работает с неблокирующими сокетами.

Цитата из справочной страницы recv;

Когда одноранговый узел потокового сокета выполнил упорядоченное завершение работы, возвращаемое значение будет 0 (традиционный возврат конца файла).

Значение 0 также может быть возвращено, если запрошенное количество байтов для приема из потокового сокета было 0.

Когда сокет доступен для чтения, я читаю его с помощью этого кода:

uint8_t buf[2048];
ssize_t rlen;
while(1){
    rlen = recv(fd, buf, sizeof(buf), 0);
    if(rlen < 0){
        /* some error came, let's close socket... */
    }
    else if(rlen == 0){
        /* is there no bytes to read? do we need break; in here? */
        /* is socket closed by peer? do we need to close socket? */
    }
    /* some code that process buf and rlen... */
}

Как мы можем узнать, в каком случае recv возвращает 0?


person fakwjdkawjdawk    schedule 22.08.2020    source источник
comment
Код с rlen == 0 никогда не будет достигнут, поскольку ваши условия while предполагают rlen != 0   -  person Steffen Ullrich    schedule 22.08.2020
comment
Кроме того, документация довольно ясна: либо соединение было закрыто, либо вы не запрашивали никаких байтов для recv, т.е. sizeof(buf) == 0   -  person Steffen Ullrich    schedule 22.08.2020
comment
@SteffenUllrich Извини, мой плохой лемм править   -  person fakwjdkawjdawk    schedule 22.08.2020
comment
Запрошенное количество байтов в вашем коде - 2048, что не равно нулю. Так что второго случая не может быть.   -  person interjay    schedule 22.08.2020
comment
При ошибке возвращается -1 и устанавливается errno. Для некоторых конкретных значений errno это означает, что данные не нужно читать.   -  person Roberto Caboni    schedule 22.08.2020


Ответы (1)


Когда recv возвращает 0, это означает, что сокет был корректно закрыт другим одноранговым узлом и также может быть закрыт с вашей стороны. Если в сокете нет данных, возвращается -1 и errno устанавливается на EAGAIN / ETIMEDOUT и сокет не должен быть закрыт.

Наконец, когда возвращается -1 и errno имеет значение, отличное от EWOULDBLOCKили EAGAIN, сокет должен быть закрыт, потому что произошла некоторая неустранимая ошибка.
Для неблокирующих сокетов это означает, что данные не доступны сразу, когда recv называется. Для блокирующих сокетов) это означает, что tgat данные недоступны даже после истечения тайм-аута (SO_RCVTIMEO), ранее установленного с помощью setsockopt().


Как вы правильно указали в своем последнем редактировании, 0 может быть возвращено из recv также, если запрошенный размер равен 0.

Как мы можем узнать, что происходит, когда recv возвращает 0?

Просто проверьте предоставленный размер recv (в данном случае это размер постоянного массива, поэтому это не имеет особого смысла; но в случае, если это переменная, поступающая откуда-то еще ...):

bufSize = sizeof(buf);

/* further code that, who knows, might affect bufSize */

rlen = read(fd, buf, bufSize);
if(rlen < 0){
    if (errno != ETIMEDOUT && errno != EWOLUDBLOCK)
    {
        /* some error came, let's close socket... */
    }
}
else if(rlen == 0){
     if (bufSize != 0)
    {
        /* Close socket */
    }
}
person Roberto Caboni    schedule 22.08.2020
comment
Разве errno ETIMEDOUT не означает, что нам тоже нужно закрыть сокет? Я полагаю EWOULDBLOCK означает there is no any data right now except recv with blocking - person fakwjdkawjdawk; 22.08.2020
comment
@fakwjdkawjdawk Вы правы. Я ошибочно имел в виду проприетарную реализацию, с которой работал, которая использовалась для возврата ETIMEDOUT в блокирующем сокете в случае истечения времени ожидания (SO_RCVTIMEO), ранее установленного с помощью setsockopt (). Я отредактирую свой ответ - person Roberto Caboni; 22.08.2020
comment
кстати, вы забыли изменить часть кода в своем ответе - person fakwjdkawjdawk; 22.08.2020
comment
sizeof никогда не возвращает 0. - person rici; 22.08.2020
comment
@rici это просто пример. также возможно получить sizeof 0 с расширением массива нулевой длины. gcc поддерживает это; int a[0]; printf("%u\n", sizeof(a)); - person fakwjdkawjdawk; 22.08.2020