1

timeoutПонадобилось реализовать построчное чтение из сокета с timeout-ом. Написал для fgets () обёртку — ifgets (). Для обеспечения timeout-a используется select (). В процессе написания столкнулся с проблемой: select()  почему-то всегда выдавал timeout, хотя данные были прочтены еще не все.

Strace показал странное поведение: мною читалась первая строка, далее был выход из функции чтения, но далее кто-то(не я) читал всё остальное содержимое открытого сокета. Естественно, при моей попытке прочитать следующую строку из сокета, по мнению select () он был пуст и происходил timeout.

Причиной это проблемы стало буферизированное чтение из сокета. Сокет опустошался в буфер, а select-у ничего не оставалось. После выключения буферизированного чтение, всё заработало как надо.

Пользоваться почти так же как fgets ():

int fd;
FILE *fds;

/* Делаем TCP сокет */
fd = socket(PF_INET, SOCK_STREAM, 0);

/* Параметры для соединения опущены */

/* Соединяемся */
connect(fd, (struct sockaddr *) &srv_i, sizeof(srv_i));

/* Переоткрываем сокет как поток */
fds =  fdopen(fd, "w+");

/* !!! ВНИМАНИЕ! Надо обязательно выключить буферизированное чтение.
Иначе всё сразу будет прочтено в буфер и
select будет возвращать только timeout
*/
setvbuf(fds, (char *) NULL, _IONBF, 0);

/* Ну а дальше вызываем ifges() и передаём timeout в сек.
и integer descriptor для select-а
*/
if (ifgets(buf, buflen, fds, timeout, fd))
      return strlen(buf);         
if (errno == ETIMEDOUT) {
     print("Socket timeout after %d seconds", timeout);
     return (-1);
}

Сама ifgets () реализована так:

char *
ifgets(char *s, int size, FILE *stream, int timeout, int fd)
{
  int res;

  if (timeout) {
      do {
          res = select_fd (fd, timeout, 0);
      } while (res == -1 && errno == EINTR);
      if (res < = 0) {
        if (res == 0) {
                errno = ETIMEDOUT;
                return NULL;
        }
      return NULL;
      }
  }
  if(fgets(s, size, stream)) {
        return s;
  }
  return NULL;
}
/* ----------------------------------------------------------------- */
static int
select_fd (int fd, int maxtime, int writep)
{
  fd_set fds, exceptfds;
  struct timeval timeout;
  FD_ZERO (&fds);
  FD_SET (fd, &fds);
  FD_ZERO (&exceptfds);
  FD_SET (fd, &exceptfds);
  timeout.tv_sec = maxtime;
  timeout.tv_usec = 0;
  /* HPUX reportedly warns here.  What is the correct incantation?  */
  return select (fd + 1, writep ? NULL : &fds, writep ? &fds : NULL,
                 &exceptfds, &timeout);
}
Google Bookmarks Digg Reddit del.icio.us Ma.gnolia Technorati Slashdot Yahoo My Web News2.ru БобрДобр.ru RUmarkz Ваау! Memori.ru rucity.com МоёМесто.ru Mister Wong