fgets () из socket-а с 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);
}









05.11.2009 в 16:01
такой рецепт обязательно в копилку!