LINUX.ORG.RU

Как проверить, есть ли байты в буфере

 , , ,


1

2

На вход программе (через Unix Domain Socket или через stdin) приходит сообщение произвольной длины, после чего отправитель ожидает ответа.

Так как сообщение произвольной длины, не получается использовать read(length) или recv(length) — если длина сообщения окажется кратной length, то случится взаимоблокировка (клиент ждёт ответа, сервер ждёт продолжения сообщения).

Решением было бы посмотреть, есть ли в буфере байты.

man ioctl предлагает такое решение:

while ((ioctl(sock, I_NREAD, &num) >= 0) && num > 0)
    /* дописать даные из буфера ввода в буфер в памяти */

, но это не работает для stdin (ошибка ENOTTY):

if ((ioctl(0, I_NREAD, &num) >= 0) && num > 0) /* ошибка ENOTTY */

Есть ли общий способ? Или для сокета и для stdin обязательно надо применять разные подходы (если так, то какие)?

EDIT: пример кода «клента» (на пхп):

$proc_handle = proc_open('./run', [['pipe', 'r'],['pipe', 'w']], $pipes);

foreach ($test_cases as $request => $response)
{
    echo "Test: ".$request;
    fwrite($pipes[0], $request);
    fflush($pipes[0]);

    echo "Answer: ".fgets($pipes[1]);
    echo "\n";
}

EDIT: Пример решения: перевод stdin в неблокирующий режим (подсказал u/Elyas, решение взято с https://stackoverflow.com/a/30548692):

#include <stdio.h>

#include <unistd.h>
#include <fcntl.h>

static int counter = 0;

void updatecounter(void)
{
	int n;
	char buf[100];
	while (1)
	{
		if ((n = read(0, buf, 100)) <= 0) break;
		counter += n;
	}
}

int main(void)
{
	fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

	while (1)
	{
		updatecounter();
		printf("%d\n", counter);
		sleep(2);
	}

	return 0;
}

EDIT: Пример решения: использование poll (подсказал u/mittorn):

#include <stdio.h>

#include <unistd.h>
#include <poll.h>

static int counter = 0;

void updatecounter(void)
{
	char buf[100];
	struct pollfd pollfds[1] = {0, POLLIN, 0};

	while (1)
	{
		if (!poll(pollfds, 1, 0)) break;
		counter += read(0, buf, 100);
	}
}

int main(void)
{
	while (1)
	{
		updatecounter();
		printf("%d\n", counter);
		sleep(2);
	}

	return 0;
}

EDIT: решение от u/kardapoltsev : указывать длину сообщения перед его началом

EDIT: оба решения проверены на сокетах.

Ответ на: комментарий от Deleted

если длина сообщения совпадает с длиной блока, то будет взаимоблокировка --- прочитается последний блок, в нём будет ровно length байтов, после чего будет попытка читать следующий блок, а его уже нет

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

то будет взаимоблокировка

Думаешь?

github.com/jgilje/v2m-player/.../tinyplayer.cpp строки 143-188.

PS: Можно проще, но там проблема с определением конца файла.

Deleted ()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

ну да, в этом примере тоже заблокируется

вот пример кода «клента» (пример на пхп):

$proc_handle = proc_open('./run', [['pipe', 'r'],['pipe', 'w']], $pipes);

foreach ($test_cases as $request => $response)
{
    echo "Test: ".$request;
    fwrite($pipes[0], $request);
    fflush($pipes[0]);

    echo "Answer: ".fgets($pipes[1]);
    echo "\n";
}
peterstein ()

«Конец сообщения» должен передаваться явно. Например можно обмениватся си-строками с 0 в конце (встретил 0 - конец сообщения). Можно придумать свой признак конца сообщения. Или сигнализировать внешними механизмами: закрытие пайпа или сокета. Все остальное - это шаманство.

anonymous ()

Общий способ - неблокирующий режим сокета. Может быть окружён удобствами от системы (select, epoll) или реализован полностью «вручную» основной цикл.

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

И называются подобные простые очень удобные протоколы TLV (type-length-value). Длина полей type+length фиксирована, поэтому эти первые байты считывается в первую очередь, а потом уже value длиной length.

gag ★★★★★ ()