Добрый день!
В широко известной книге Стивенса "UNIX разработка сетевых
приложений", есть следующий код:
01: ssize_t writen(int fd, const void *vptr, size_t n)
02: {
03: size_t nleft;
04: ssize_t nwritten;
05: const char *ptr;
06:
07: ptr = vptr;
08: nleft = n;
09: while(nleft > 0) {
10: if((nwritten = write(fd, ptr, nleft)) <= 0) {
11: if(errno == EINTR) nwritten = 0;
12: else return -1;
13: }
14: nleft -= nwritten;
15: ptr += nwritten;
16: }
17: return n;
18: }
Мой вопрос касается строки под номером 10. Представим себе ситуацию,
когда узел закрывает сокет. Тогда write() вернет 0 и дальнейшее
поведение программы будет зависеть от значения errno, которое может
оказаться любым. Т.к. ошибка в книжке маловероятна, хотелось бы, чтобы
знающие люди подсказали где ошибка в моих рассуждениях.
> Представим себе ситуацию, когда узел закрывает сокет. Тогда write() вернет 0 и дальнейшее поведение программы будет зависеть от значения errno, которое может оказаться любым.
правильно
> Т.к. ошибка в книжке маловероятна,
правильно
> хотелось бы, чтобы знающие люди подсказали где ошибка в моих рассуждениях.
--- cut ---
Where this volume of IEEE Std 1003.1-2001 requires -1 to be returned and errno set to [EAGAIN], most historical implementations return zero (with the O_NDELAY flag set, which is the historical predecessor of O_NONBLOCK, but is not itself in this volume of IEEE Std 1003.1-2001). The error indications in this volume of IEEE Std 1003.1-2001 were chosen so that an application can distinguish these cases from end-of-file. While write() cannot receive an indication of end-of-file, read() can, and the two functions have similar return values. Also, some existing systems (for example, Eighth Edition) permit a write of zero bytes to mean that the reader should get an end-of-file indication; for those systems, a return value of zero from write() indicates a successful write of an end-of-file indication.
--- cut ---
так что получение нуля скорее означает явное указание на EOF и AFAIU никто не гарантирует, что errno в этом случае будет выставлен.
ps: тем более, что при указанном методе обработки EINTR все попытки прервать ваше приложение, зависшее в мертвом цикле на write по сигналу ни к чему не приведут, что не есть хорошо :-/
В общем-то, такой способ обработки EINTR меня устраивает :). Выходит, все-таки ошибка в книге? Странно, в readn() из той же книги все ОК. Может, все-таки, какая-то тонкость есть?
> Да, там результат read() отдельно проверяется на < 0 и отдельно на 0.
допустим, судя по коду, NetBSD вернет 0 из write(2) в случае, если какая-то часть данных была уже записана [но не вся] но в процессе записи процесс был прерван по сигналу. очевидно, что значение errno в этом случае не меняется и что там будет лишь Аллаху известно.
А почему в NetBSD возвращается 0? Мне кажется, что в случае, когда часть данных уже записана и процесс прерывается по сигналу, write() должен вернуть количество отправленных байт. В противном случае, не понятно, сколько байт отправлено до получения сигнала и что делать дальше?...
Ну так что, в качестве резюме дискуссии - ошибка в книге? Нужно отдельно проверять код возврата на 0 (завершение соединения) и отдельно на -1 (возникла ошибка).
> Ну так что, в качестве резюме дискуссии - ошибка в книге? Нужно отдельно проверять код возврата на 0 (завершение соединения) и отдельно на -1 (возникла ошибка).
я бы отдельно проверил на 0 и отдельно на -1. затрат это не требует никаких, зато совесть чиста.