У меня есть простой сервер tcp/ip, написанный на C++ на linux. Я использую асинхронные сокеты и epoll. Можно ли узнать, сколько байтов доступно для чтения, когда я получаю событие EPOLLIN?Как я могу получить количество байтов, доступных для чтения в асинхронном сокете на linux?
ответ
От man 7 tcp
:
int value;
error = ioctl(sock, FIONREAD, &value);
SIOCINQ
Или же, который является синонимом FIONREAD
.
В любом случае, я бы рекомендовал использовать recv
в неблокирующем режиме в цикле до тех пор, пока он не вернет EWOULDBLOCK
.
UPDATE:
Из ваших комментариев ниже я думаю, что это не подходящее решение для вашей проблемы.
Представьте, что ваш заголовок имеет 8 байтов, и вы получаете только 4; то ваш poll/select
вернет EPOLLIN
, вы проверите FIONREAD
, увидите, что заголовок еще не завершен и не используется для большего количества байтов. Но эти байты никогда не поступают, поэтому вы продолжаете получать EPOLLIN
при каждом звонке до poll/select
, и у вас нет занятой петли без операции. То есть, poll/select
являются с уровнем запуска. Не то, чтобы функция, вызванная краем, также решает вашу проблему.
В конце вы намного лучше выполняете немного работы, добавляя буфер на соединение и ставя в очередь байты, пока не получите достаточно. Это не так сложно, как кажется, и работает намного лучше. Например, что-то вроде этого:
struct ConnectionData
{
int sck;
std::vector<uint8_t> buffer;
size_t offset, pending;
};
void OnPollIn(ConnectionData *d)
{
int res = recv(d->sck, d->buffer.data() + offset, d->pending);
if (res < 0)
handle_error();
d->offset += res;
d->pending -= res;
if (d->pending == 0)
DoSomethingUseful(d);
}
И всякий раз, когда вы хотите, чтобы получить количество байт:
void PrepareToRecv(ConnectionData *d, size_t size)
{
d->buffer.resize(size);
d->offset = 0;
d->pending = size;
}
Спасибо за ответ! Да, я могу использовать recv или читать как это, но у меня есть двоичный протокол, который хранит размер пакета в его заголовке, поэтому я подумал, что код может стать намного проще, если я проверю, доступен ли заголовок для чтения или не. –
@PavelDavydov: Действительно, наличие буфера и накопление здесь может быть (просто) немного сложным. Но я думаю, что вы можете срезать слишком много углов ... пожалуйста, посмотрите мое обновление. – rodrigo
Да, получение EPOLLIN бесконечно может быть проблемой, спасибо. Мой сервер действует так же, как тот, который вы рекомендуете, я просто подумал, что, возможно, это можно сделать проще и чище. –
Почему? Функция recv() сообщит вам и предоставит вам данные. – EJP
Ну, иногда может быть довольно удобно знать размер буфера, который должен быть выделен для чтения. Например, в kqueue количество байтов возвращается в поле данных. –
IMHO вы не должны выделять буфер для чтения. Это просто создает мусор, фрагментацию кучи и т. Д. Вы должны использовать локально выделенный массив байтов. – EJP