Итерация чтения () из сокета - PullRequest
3 голосов
/ 10 апреля 2009

Это правильный способ перебрать read в сокете? Мне трудно заставить это работать должным образом. data.size - это unsigned int, который также заполняется из сокета. Это правильно. data.data является unsigned char *.

if ( data.size > 0 ) {
    data.data = (unsigned char*)malloc(data.size);
    memset(&data.data, 0, data.size);
    int remainingSize = data.size;
    unsigned char *iter = data.data;
    int count = 0;
    do {
        count = read(connect_fd, iter, remainingSize);
        iter += count;
        remainingSize -= count;
    } while (count > 0 && remainingSize > 0);
}
else {
    data.data = 0;
}

Заранее спасибо.

Ответы [ 4 ]

9 голосов
/ 10 апреля 2009

Вам нужно проверить возвращаемое значение из прочитанного, прежде чем начинать добавлять его к другим значениям.

Вы получите ноль, когда сокет сообщает EOF, и -1 при ошибке. Имейте в виду, что для сокета EOF отличается от закрытого.

3 голосов
/ 14 апреля 2009

Имейте в виду, что вызовы read () являются системными вызовами и, следовательно, источником возможного блокирования, и даже если вы используете неблокирующий ввод / вывод, они по своей природе тяжелые. Я бы порекомендовал свести их к минимуму.

Хороший путь, который всегда помог мне в течение более чем десяти лет программирования сокетов BSD на C, - это использовать неблокирующий ввод-вывод и выполнить FIONREAD ioctl (), чтобы получить общий объем данных, ожидающих при заданный интервал опроса (при условии, что вы используете какой-то синхронный мультиплексор ввода-вывода, такой как select ()), а затем просто читаете () столько раз, сколько необходимо для захвата всего этого, а затем возвращаете функцию на данный момент до следующий таймер.

3 голосов
/ 10 апреля 2009

Поместите чтение как часть условия while.

while((remainingSize > 0) && (count = read(connect_fd, iter, remainingSize)) > 0)
{
    iter += count;
    remainingSize -= count;
}

Таким образом, в случае неудачи вы немедленно останавливаете цикл.
Это очень распространенный способ использовать чтение как часть условия цикла, в противном случае вам нужно проверить состояние внутри цикла, что делает код более уродливым.

Лично:
Я бы переместил весь вышеописанный тест в отдельную функцию для удобства чтения, но ваш пробег может быть очень большим.

Также использование malloc (и компании) приведет к целому ряду проблем управления памятью. Я бы использовал std :: vector. Это также в будущем подтверждает код, когда вы измените его, чтобы начать генерировать исключения, теперь он также будет безопасным для исключений.

Итак, если вы измените data.data, чтобы иметь тип std :: vector затем

if ( data.size > 0 )
{
    std::vector<unsigned char>   buffer(data.size);

    unsigned char *iter = &buffer[0];
    while(...  read(connect_fd, iter, remainingSize) )
    {
        .....
    }

    ... handle error as required

    buffer.resize(buffer.size() - remainingSize);
    data.data.swap(buffer);
}
3 голосов
/ 10 апреля 2009

Программирование сокетов низкого уровня очень утомительно и подвержено ошибкам. Если вы используете C ++, вы должны попытаться использовать библиотеки более высокого уровня, такие как Boost или ACE .

Я бы также предложил прочитать Сетевое программирование на C ++: освоение сложности с использованием ACE и шаблонов и Сетевое программирование на C ++: систематическое повторное использование с ACE и Frameworks

...