LINUX.ORG.RU

Неустоичивая работа сокетов в O_NONBLOCK режиме


0

0

Пара вопросов знающим людям:
1) Что делает fcntl(sock, F_SETFL, O_NONBLOCK) с дескриптором на самом низком уровне? Просто флаг устанавливает?
2) O_NONBLOCK действует только на Send/Write и Recv/Read, или распространяется на всю работу с дескриптором?
3) Если errno устанавливается в EAGAIN при наличии возможности блокирования, то почему это ошибка?
4) Правильно ли игнорировать EAGAIN?
5) Как это действует на select/poll?

Red Hat Linux release 6.2 (Zoot)
Linux 2.2.24-6.2.3 i686

Вобщем, я столкнулся с неустойчивым приемом данных через
неблокирующий сокет (), например, пропадают пакеты или
приходят в искаженном виде. Особенно, это проявляется при
двойном вызове recv, т.е. сначала принимаем размер пакета,
а потом сам пакет.

Send size ---> Recv size
Send data ---> Recv data

Псевдокод Send/Recv:

int [Send/Recv](sock, void* buf, int size)
{
int count = 0, bytes = -1;
... проверка параметров ...

while(count < size)
{
bytes = [send/recv](sock, buf + count, size - count, 0);
if(bytes < 0)
{
if(errno == EAGAIN)
break;
return -1;
}
count += bytes;
}

return count;
}

Даже если отлавливать события POLLIN в poll,
то пакет или искажается или теряется:

int HandleRecv(int sock, void* buf, int size)
{
int status = -1, bytes = 0;
struct pollfd fds[1];
... проверка параметров ...

fds[0].fd = sock;
fds[0].events = POLLIN;

if( (status = poll(fds, 1, 10000)) > 0 )
{
if( fds[0].revents & POLLIN ) {
bytes = Recv(sock, buf, size);
} else if( fds[0].revents & POLLHUP ) {
return -1;
} else if( fds[0].revents & POLLNVAL ) {
return -1;
} else if( fds[0].revents & POLLERR ) {
return -1;
}
} else {
return -1;
}

return bytes;
}



anonymous

1) Что делает fcntl(sock, F_SETFL, O_NONBLOCK) с дескриптором на самом низком уровне? Просто флаг устанавливает?

Смотри исходники ядра.

2) O_NONBLOCK действует только на Send/Write и Recv/Read, или распространяется на всю работу с дескриптором?

Нет еще как минимум на connect. =) И accept.

3) Если errno устанавливается в EAGAIN при наличии возможности блокирования, то почему это ошибка?

Это значит что в то время пока процесс был заблокирован в системном вызове пришел сигнал и блокирование прекратилось.

4) Правильно ли игнорировать EAGAIN?

Обычно надо просто повторить вызов котрый это вернул.

5) Как это действует на select/poll?

select и poll по любому блокируют процесс, до тех пор пока не будет события на дескрипторе.

OxiD ★★★★
()

>в O_NONBLOCK режиме

А зачем он вообще тебе нужен. Особенно если ты юзаеш poll/select ??

cvv ★★★★★
()
Ответ на: комментарий от OxiD

"
......................
3) Если errno устанавливается в EAGAIN при наличии возможности блокирования, то почему это ошибка?

Это значит что в то время пока процесс был заблокирован в системном вызове пришел сигнал и блокирование прекратилось.

4) Правильно ли игнорировать EAGAIN?

Обычно надо просто повторить вызов котрый это вернул.
...................
"

Ты случаем EAGAIN с EINTR не путаешь?


Dead ★★★★
()
Ответ на: комментарий от Dead

>Ты случаем EAGAIN с EINTR не путаешь?

Путает. точно путает. Интересно почему мозги заклинило или ???

cvv ★★★★★
()
Ответ на: комментарий от Dead

EAGAIN это не EINTR в данном контексте.

А неблокирующий сокет эффективен при рассылке пакета по списку дескрипторов, а в блокирующем режиме все стопорится на плохом канале из-за блокировки send, причем ваять таймеры для потоков весьма убыточно - не иначе как в новом потоке таймер пускать.

Таким образом: NONBLOCK + poll = асинхронные сокеты для потоков.

Классический вариант через сигналы, для потоков бесполезен, все потоки получают сигнал хочешь ли ты это или нет - кривость реализации pthread :)

anonymous
()
Ответ на: комментарий от anonymous

Ты чёто не того.

Ситуация EAGAIN не может возникнуть на блокирующем сокете. тем более в следствии прихода сигнала.

А poll собственно для того и существует чтобы не блокировались блокирующие сокеты

cvv ★★★★★
()
Ответ на: комментарий от cvv

2 cvv:

> > в O_NONBLOCK режиме
>
> А зачем он вообще тебе нужен. Особенно если ты юзаеш poll/select ??
> ...
> А poll собственно для того и существует чтобы не блокировались
> блокирующие сокеты

успешный возврат poll/select _не_ гарантирует, что socket без
O_NONBLOCK не будет блокирован.

idle ★★★★★
()
Ответ на: комментарий от idle

>успешный возврат poll/select _не_ гарантирует, что socket без O_NONBLOCK не будет блокирован.

Вообщето да, если размер блока данных переданный send больше чем свободное место в буфере.

А так одно из назначений poll именно устранить нежелательное блокирование

cvv ★★★★★
()
Ответ на: комментарий от cvv

> Вообщето да, если размер блока данных переданный send

не только

> А так одно из назначений poll именно устранить нежелательное
> блокирование

скорее, все таки, успешный возврат poll означает, что имеет
смысл попытаться сделать неблокирующую операцию i/o сейчас,
и может быть, она не вернет EAGAIN.

idle ★★★★★
()
Ответ на: комментарий от idle

>> Вообщето да, если размер блока данных переданный send

>не только

>> А так одно из назначений poll именно устранить нежелательное >> блокирование

>скорее, все таки, успешный возврат poll означает, что имеет >смысл попытаться сделать неблокирующую операцию i/o сейчас, >и может быть, она не вернет EAGAIN.

А можно по подробней???

cvv ★★★★★
()
Ответ на: комментарий от cvv

> А можно по подробней???

а что именно?

anonymous
()
Ответ на: комментарий от idle

Да, сигналы я перепутал 8(

Вот теперь у меня вопрос возник.. Насчет потоков. Если один из них будет заблокировак в системном вызове, то это заблокирует остальные потоки? А то я совсем запутался что-то. Раньше думал что не должно.

OxiD ★★★★
()
Ответ на: комментарий от anonymous

К Автору
Искажения ?
У тебя по ходу код кривой до изнеможения
скорее всего recv тебе возвращает EAGAIN
а ты думаешь что данные получены, и т.д короче
обработка ошибок и логика в програме херовая....

anonymous
()
Ответ на: комментарий от anonymous

С логикой все ок, как сказали выше, по EAGAIN повторяем вызов. Нули ыот из recv часто возвращаются, подозрение, что из-за одновременного Send/Recv в один сокет, хотя вроде как пересекаться не должны. Например вивсит poll с Recv, и тут Send посылает на тот-же сокет -> Recv возвращает 0 (это подозрение).

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.