2013-07-31 4 views
2

У меня есть простой сервер tcp/ip, написанный на C++ на linux. Я использую асинхронные сокеты и epoll. Можно ли узнать, сколько байтов доступно для чтения, когда я получаю событие EPOLLIN?Как я могу получить количество байтов, доступных для чтения в асинхронном сокете на linux?

+0

Почему? Функция recv() сообщит вам и предоставит вам данные. – EJP

+0

Ну, иногда может быть довольно удобно знать размер буфера, который должен быть выделен для чтения. Например, в kqueue количество байтов возвращается в поле данных. –

+0

IMHO вы не должны выделять буфер для чтения. Это просто создает мусор, фрагментацию кучи и т. Д. Вы должны использовать локально выделенный массив байтов. – EJP

ответ

7

От 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; 
} 
+0

Спасибо за ответ! Да, я могу использовать recv или читать как это, но у меня есть двоичный протокол, который хранит размер пакета в его заголовке, поэтому я подумал, что код может стать намного проще, если я проверю, доступен ли заголовок для чтения или не. –

+0

@PavelDavydov: Действительно, наличие буфера и накопление здесь может быть (просто) немного сложным. Но я думаю, что вы можете срезать слишком много углов ... пожалуйста, посмотрите мое обновление. – rodrigo

+0

Да, получение EPOLLIN бесконечно может быть проблемой, спасибо. Мой сервер действует так же, как тот, который вы рекомендуете, я просто подумал, что, возможно, это можно сделать проще и чище. –

 Смежные вопросы

  • Нет связанных вопросов^_^