Вопрос по sockets, c, gcc, linux – Есть ли более надежный способ надежного использования функции write ()?

6

Я прочиталman страницы, и я понимаю, что еслиwrite() не удается и устанавливаетerrno вEAGAIN или жеEINTRЯ могу выполнитьwrite() снова, поэтому я придумал следующий код:

<code>  ret = 0;
  while(ret != count) {
    write_count = write(connFD, (char *)buf + ret,  count);
    while (write_count < 0) {
      switch(errno) {
        case EINTR:
        case EAGAIN:
          write_count = write(connFD, (char *)buf + ret,  count -ret);
          break;
        default:
          printf("\n The value of ret is : %d\n", ret);
          printf("\n The error number is : %d\n", errno);      
          ASSERT(0);
      }
    }
    ret += write_count;
  }
</code>

Я выступаюread() а такжеwrite() на розетках и обработкеread() так же, как и выше. Я использую Linux, сgcc компилятор.

@ Иоанн Вы правы. Это не может происходить на блокирующих гнездах. user207421
Вот почему я упомянул об этом, на всякий случай. Я проверил EAGAIN только на неблокирующих сокетах, так как я понял, что это работает. Если кто-то знает, что это происходит при блокировке сокетов, говорите. Ioan
@loan Я использую блокирующие розетки. Я не знал, что проверку на EAGAIN необходимо выполнять только в том случае, если мы используем неблокирующие сокеты. AnkurVj
Проверка на EAGAIN будет означать, что вы используете неблокирующие сокеты. Я мог бы абстрагировать это в отдельную функцию, которая предоставляет статистику того, сколько было написано и плохо ли это. Ioan
Вы можете использовать вызовы select (), которые уведомят вас, как только сокет будет готов к записи. Sumit Trehan

Ваш Ответ

4   ответа
0

write(): класс функций записи, принимающихFILE* в качестве аргумента. То есть, самое главное,fprintf() а такжеfwrite(), Внутри эти библиотечные функции используютwrite() Syscall, чтобы сделать свою работу, и они обрабатывают такие вещи, какEAGAIN а такжеEINTR.

Если у вас есть только файловый дескриптор, вы всегда можете обернуть его вFILE* посредствомfdopen(), так что вы можете использовать его с функциями выше.

Тем не менее, есть одна ловушка:FILE* потоки обычно буферизируются. Это может быть проблемой, если вы общаетесь с какой-то другой программой и ожидаете ее ответа. Это может заблокировать обе программы, даже если логическая ошибка отсутствует, просто потому, чтоfprintf() решил отложить соответствующийwrite() немного. Вы можете отключить буферизацию илиfflush() выходные потоки всякий раз, когда вам действительно нужноwrite() звонки должны быть выполнены.

3

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

Как только вы нажметеEAGAIN Вы, вероятно, захотите немного поспать или вернуть управление в цикл обработки событий (при условии, что вы его используете).

СEINTR Ситуация немного другая. Если ваше приложение получает сигналы непрерывно, то это может быть проблемой в вашем приложении или среде, и по этой причине я склонен иметь какой-то внутреннийeintr_max счетчик, поэтому я не застрял в теоретической ситуации, где я просто продолжаю бесконечно зацикливаться наEINTR.

Ответ Альнитака (достаточный для большинства случаев) также должен быть сохраненerrno где-то, как это может быть забитperror() (хотя это могло быть опущено для краткости).

Error: User Rate Limit ExceededneverError: User Rate Limit Exceededif the call were executed in blocking mode,Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededEAGAINError: User Rate Limit ExceededEWOULDBLOCKError: User Rate Limit ExceededmanError: User Rate Limit ExceededPOSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.Error: User Rate Limit ExceededEAGAINError: User Rate Limit ExceededO_NONBLOCKError: User Rate Limit Exceeded
Error: User Rate Limit Exceededman 2 readError: User Rate Limit ExceededERRORError: User Rate Limit ExceededThe file descriptor fd refers to a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.Error: User Rate Limit Exceededif a call were executed in blocking modeError: User Rate Limit Exceededexecuting in blocking modeError: User Rate Limit Exceeded
2

poll дескриптор в случаеEAGAIN вместо того, чтобы просто зацикливаться и перегружать процессор без веской причины. Это своего рода «блокирующая оболочка» для неблокированияwrite Я использую:

ssize_t written = 0;

while (written < to_write) {
    ssize_t result;
    if ((result = write(fd, buffer, to_write - written)) < 0) {
        if (errno == EAGAIN) {
            struct pollfd pfd = { .fd = fd, .events = POLLOUT };
            if (poll(&pfd,, 1, -1) <= 0 && errno != EAGAIN) {
                break;
            }
            continue;
        }
        return written ? written : result;
    }
    written += result;
    buffer += result;
}
return written;

Обратите внимание, что я на самом деле не проверяю результатыpoll кроме возвращаемого значения; Я полагаю следующееwrite потерпит неудачу, если в дескрипторе будет постоянная ошибка.

Вы можете включитьEINTR как повторяющуюся ошибку, просто добавив ее в условия сEAGAIN, но я предпочитаю прерывать ввод-вывод.

6

двух отдельных вызовахwriteни для двух вложенных циклов.

Мой обычный цикл будет выглядеть примерно так:

for (int n = 0; n < count; ) {
    int ret = write(fd, (char *)buf + n, count - n);
    if (ret < 0) {
         if (errno == EINTR || errno == EAGAIN) continue; // try again
         perror("write");
         break;
    } else {
        n += ret;
    }
}

// if (n < count) here some error occurred
Error: User Rate Limit Exceededen.wikipedia.org/wiki/Don't_repeat_yourself
Error: User Rate Limit Exceeded AnkurVj
Error: User Rate Limit ExceededSA_RESTARTError: User Rate Limit ExceededEINTRError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded'The Pragmatic Programmer'Error: User Rate Limit ExceededwriteError: User Rate Limit Exceeded
Error: User Rate Limit ExceededEAGAIN/EWOULDBLOCK.Error: User Rate Limit Exceededselect()Error: User Rate Limit Exceeded

Похожие вопросы