Home > itblogs > Задачка на интервью. Пакеты.

Задачка на интервью. Пакеты.

February 8th, 2011

А хотите задачку, которую я даю претендентам на контрактную позицию в нашей команде? Вот она, родимая. Есть библиотечная функция:

char get_byte();

Функция читает байт из некоего соединения. При необходимости, функция ждет, пока не придет очередной байт. По соединению пересылаются пакеты следующего формата: два байта длины, за которыми следует данные. Длина пакета включает в себя заголовок пакета, т.е. те самые два байта.

Формат пакета.

Формат пакета.

Нужно написать функцию recv, получающую очередной пакет:

size_t recv(char* buf, size_t size);

В buf передается указатель на буфер, куда должен быть записан пакет (полностью, включая заголовок). size указывает размер этого буфера. Функция возвращает полную длину пакета (включая заголовок) если пакет получен успешно. В случае ошибки возвращается 0.

Соединение, по которому пересылаются байты, не имеет помех. Посылающая сторона ведет себя хорошо и не подсовывает мусор. Функция recv вызывается на границе пакетов. Других потоков нет. Исключений нет.

Написать на доске работающий код для этой задачки удается не более, чем одному из пяти претендентов. По крайней мере за последние пару месяцев.

  1. Bogdan
    February 8th, 2011 at 22:35 | #1

    Just for fun, раз уж комментов пока нет:

    size_t recv(char *buf, size_t size)
    {
    short int length;

    ((char*)&length)[0] = get_byte();
    ((char*)&length)[1] = get_byte();

    if(size < length)
    {
    for(int i = 0; i < length – 2; i++)
    get_byte();
    return 0;
    }
    else
    {
    ((short int*)buf)[0] = length;
    for(int i = 2; i < length; i++)
    buf[i] = get_byte();
    return length;
    }
    }

    Так как не совсем понятно, что считать ошибкой при указанных условиях, я считаю, что ошибкой является недостаточный размер буфера. Пакет в этом случае я все равно вычитываю весь, потому что функций для запихивания байтов обратно нет.

    Написал в текстовом редакторе, не проверял и даже не компилировал ) Ну что, какие ошибки?

  2. February 8th, 2011 at 22:36 | #2

    Возможно я что-то не так понимаю, но это же довольно простая задача, студентам на зачёте можно давать в качестве вопроса на три?

  3. Torvin
    February 8th, 2011 at 22:38 | #3

    get_byte должна возвращать int а не char, иначе как тогда отлавливать конец потока?

  4. Tyler
    February 8th, 2011 at 22:39 | #4

    в чём сложность?
    имхо, только в определении LE/BE единственного поля.

    size_t recv(char* buf, size_t size)
    {
    uint16_t len, i;
    uint8_t hdr1, hdr2;

    // size как минимум 2, под заголовок
    if (!buf || size < 2)
    return 0;
    // получаем заголовок
    hdr1 = get_byte();
    hdr2 = get_byte();
    // превращаем 2 разрозненных байта в одно число
    #ifdef _LITTLE_ENDIAN_
    len = hdr1 || (hdr2 << 8);
    #else
    len = hdr2 || (hdr1 << 8);
    #endif
    if (size < len)
    return 0;
    // сохраняем в буффер
    *buf = hdr1;
    buf++;
    *buf = hdr2;
    buf++;
    // забираем пакет
    for (i = 2; i < len; i++)
    {
    *buf = get_byte();
    buf++;
    }
    return len;
    }

  5. February 8th, 2011 at 22:49 | #5

    Функции recv придется хранить контекст где-то.
    Потому что размер буфера может быть меньше, чем размер пакета, но чтобы узнать это – мы должны вычитать два байта из входного потока, а функции “вернутся назад на два байта, чтобы поток остался на границе пакета” я что-то не вижу. Т.е. при ошибке мы должны где-то сохранить размер пакета и флаг, что мы находимся во входном потоке не на границе пакета, а после его заголовка.

  6. Nikolay T.
    February 8th, 2011 at 22:54 | #6

    size_t recv(char* buf, size_t size) {
    char bSize, bData;
    size_t uBufPos;
    if(size<1) { return 0; }
    uBufPos=0;
    bSize = get_byte();
    buf[uBufPos] = bSize; uBufPos++;
    while (uBufPos=size) { return 0; }
    bData = get_byte();
    buf[uBufPos] = bData;
    uBufPos++;
    }
    return bSize;
    }

  7. Александр
    February 8th, 2011 at 23:01 | #7

    > size_t recv(char* buf, size_t size);

    Тогда, наверное, char** buf. Ведь нам нужно передать в функцию указатель не на один char, а на блок char*. Хотя разницы никакой, адрес есть адрес.

  8. February 8th, 2011 at 23:24 | #8

    Опущено ключевое условие – не сказано о таймауте. Без этого сложно будет корректно написать функцию.

  9. Nikolay T.
    February 8th, 2011 at 23:24 | #9

    Кривокомментарий сожрал часть кода, вот правильно.

    size_t recv(char* buf, size_t size) {
    char bSize, bData;
    size_t uBufPos;
    if(size<1) { return 0; }
    uBufPos=0;
    bSize = get_byte();
    buf[uBufPos] = bSize; uBufPos++;
    while (uBufPos<=size) {
    if(size<=uBufPos) { return 0; }
    bData = get_byte();
    buf[uBufPos] = bData;
    uBufPos++;
    }
    return bSize;
    }

  10. Nikolay T.
    February 8th, 2011 at 23:27 | #10

    Тьфу, с ошибкой восстановил, вот правильно.
    size_t recv(char* buf, size_t size) {
    char bSize, bData;
    size_t uBufPos;
    if(size<1) { return 0; }
    uBufPos=0;
    bSize = get_byte();
    buf[uBufPos] = bSize; uBufPos++;
    while (uBufPos<=bSize) {
    if(size<=uBufPos) { return 0; }
    bData = get_byte();
    buf[uBufPos] = bData;
    uBufPos++;
    }
    return bSize;
    }

  11. sergey
    February 8th, 2011 at 23:36 | #11

    А если буфер слишком мал как об этом сказать пользователю?

  12. February 8th, 2011 at 23:43 | #12

    size_t recv(char *buf, size_t size)
    {
    size_t bufIdx = 2;
    size_t length;

    if (size < 2)
    return 0;

    buf[0] = get_byte();
    buf[1] = get_byte();
    length = ntohs(*((unsigned short*)buf));

    while (bufIdx < length && bufIdx < size) {
    buf[bufIdx] = get_byte();
    bufIdx++;
    }

    return bufIdx;
    }

  13. February 8th, 2011 at 23:55 | #13

    Виноват, невнимательно прочитал условие.

    size_t recv(char *buf, size_t size)
    {
    size_t bufIdx = 2;
    char lenBuf[2];
    size_t length;

    // чтобы по возможности не портить состояние потока
    if (!buf || size < 2)
    return 0;

    lenBuf[0] = get_byte();
    lenBuf[1] = get_byte();
    length = ntohs(*((unsigned short*)lenBuf));

    // ничего не поделаешь, состояние потока испорчено
    if (size < length)
    return 0;

    while (bufIdx < length) {
    buf[bufIdx] = get_byte();
    bufIdx++;
    }

    return bufIdx;
    }

  14. Сергей
    February 8th, 2011 at 23:57 | #14

    я написал так, но камменты натолкнули на интересную мысль, если буфер меньше чем пакет, наверное нужно съесть пакет, хотя по хорошему пожалуй стоило бы откатить 2 байта длины пакета в поток и вернуть ошибку

    #include

    char buf[] = {2, 0, 1, 2};
    int bufPos = 0;
    char get_byte()
    {
    return buf[bufPos++];
    }

    size_t recv(char *buf, size_t size)
    {
    if(!buf || size < 2)
    return 0;

    size_t lowLen = get_byte();
    size_t highLen = get_byte();

    size_t length = highLen << 8 | lowLen;
    if(length + 2 <= size) {
    (*buf++) = lowLen;
    (*buf++) = highLen;
    int i;
    for(i = 0; i < length; i++)
    (*buf++) = get_byte();

    return length + 2;
    }

    return 0;
    }

    int main()
    {
    char * buf = (char *)malloc(4);
    size_t len = recv(buf, 4);
    printf("Size: %d\nData: %d, %d, %d, %d\n", (int)len, buf[0], buf[1], buf[2], buf[3]);
    free(buf);
    }

  15. February 9th, 2011 at 00:00 | #15

    Блин, да что же я такое пишу!

    size_t recv(char *buf, size_t size)
    {
    size_t bufIdx = 2;
    size_t length;

    // чтобы по возможности не портить состояние потока, если даже длину
    // некуда прочесть
    if (!buf || size < 2)
    return 0;

    buf[0] = get_byte();
    buf[1] = get_byte();
    length = ntohs(*((unsigned short*)buf));

    // писать пакет некуда – ничего не поделаешь, состояние потока испорчено;
    // по-хорошему, надо прочитанные два байта засунуть обратно в поток
    if (size < length)
    return 0;

    while (bufIdx < length) {
    buf[bufIdx] = get_byte();
    bufIdx++;
    }

    return length;
    }

  16. Игорь
    February 9th, 2011 at 00:24 | #16

    Ой, а зачем так сложно-то делают все? Можно же просто в лоб сделать, и при этом не терять данные в случае маленького буфера.

    size_t recv(char *buf, size_t size)
    {
    if(size < 2) return 0;
    static WORD len = 0;
    if(!len)
    {
    buf[0] = getbyte();
    buf[1] = getbyte();
    len = buf[0] + buf[1] << 8;
    }
    if(size < len)
    {
    return 0;
    }
    for(WORD i = 2; i < len; ++i)
    {
    buf[i] = getbyte();
    }
    return len;
    }

  17. Misha
    February 9th, 2011 at 00:38 | #17

    size_t recv(char* buf, size_t size)
    {
    unsigned short packSize = (get_byte(0) < size)
    {
    return 0;
    }
    buf[0] = (char)(packSize >> 8);
    buf[1] = (char)packSize;
    unsigned short index = 2;
    while (packSize > index)
    {
    buf[index] = get_byte(3);
    index++;
    }
    return packSize;
    }

  18. Misha
    February 9th, 2011 at 00:42 | #18

    обрезало “|”

    тогда так

    size_t recv(char* buf, size_t size)
    {
    unsigned short packSize = (get_byte() < size)
    {
    return 0;
    }
    buf[0] = (char)(packSize >> 8);
    buf[1] = (char)packSize;
    unsigned short index = 2;
    while (packSize > index)
    {
    buf[index] = get_byte();
    index++;
    }
    return packSize;
    }

  19. Misha
    February 9th, 2011 at 00:44 | #19

    @Misha
    странно ведет себя коментарии с “|” :(

    size_t recv(char* buf, size_t size)
    {
    unsigned short packSize = (get_byte() < size)
    {
    return 0;
    }
    buf[0] = (char)(packSize >> 8);
    buf[1] = (char)packSize;
    unsigned short index = 2;
    while (packSize > index)
    {
    buf[index] = get_byte();
    index++;
    }
    return packSize;
    }

  20. Misha
    February 9th, 2011 at 00:46 | #20

    @Misha
    Все равно обрезал, тогда так

    size_t recv(char* buf, size_t size)
    {
    unsigned short packSize = (get_byte() < size)
    {
    return 0;
    }
    buf[0] = (char)(packSize >> 8);
    buf[1] = (char)packSize;
    unsigned short index = 2;
    while (packSize > index)
    {
    buf[index] = get_byte();
    index++;
    }
    return packSize;
    }

  21. iddqd
    February 9th, 2011 at 01:21 | #21

    собственно нужно знать нэйтивный порядок байт на целевой платформе

  22. Anonymous
    February 9th, 2011 at 01:23 | #22

    Александр Бакулин :
    length = ntohs(*((unsigned short*)buf));

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

  23. Alexander
    February 9th, 2011 at 01:49 | #23

    @Bogdan

    Почти уверен, что у вас решение имеет изъяв в виде разницы low endian и high-endian архитектур

  24. Anonymous
    February 9th, 2011 at 02:11 | #24

    для gcc, msvs не скушает __attribute__ и char data[0]

    struct packet_
    {
    uint16_t size;
    char data[0];
    } __attribute__ ((packed));

    typedef struct packet_ packet_t;

    size_t recv(char* buffer, size_t size)
    {
    if (size size = ( (uint8_t)get_byte() <size = ntohs( packet->size ); // если надо

    if (size size)
    return 0;

    for(size_t i=0, data_size = packet->size-2 ; i data[i] = get_byte();
    }

    return packet->size;
    }

  25. dnk
    February 9th, 2011 at 02:12 | #25

    для gcc, msvs не скушает __attribute__ и char data[0]
    написано на си

    struct packet_
    {
    uint16_t size;
    char data[0];
    } __attribute__ ((packed));

    typedef struct packet_ packet_t;

    size_t recv(char* buffer, size_t size)
    {
    if (size size = ( (uint8_t)get_byte() <size = ntohs( packet->size ); // если надо

    if (size size)
    return 0;

    for(size_t i=0, data_size = packet->size-2 ; i data[i] = get_byte();
    }

    return packet->size;
    }

  26. dnk
    February 9th, 2011 at 02:14 | #26

    @dnk
    парсер покусал код, http://pastebin.com/nBVqYEra

  27. Max A.
    February 9th, 2011 at 02:44 | #27

    @Tyler функция не реентерабельна

    @Nikolay T. размер содержат первые ДВА байта

    @Александр Бакулин портите исходный буфер, нужно только при возможности писать в него

    Я уж думал удивиться, что таковых (решающих) мало находится,
    а здесь такой фейспалм, что я признал себя наивным.
    LE/BE – это то, о чём не подумал я, если что.

    А предварительных тестовых заданий у вас не проводится?
    Отчего все уже на доске это пишут, время отнимают?

  28. February 9th, 2011 at 02:51 | #28

    Ну вот, Алексей, вон, сколько умных людей сходу решили Вашу задачку! Глядишь, так и подберете себе сотрудника. :)

  29. Max A.
    February 9th, 2011 at 02:54 | #29

    Это кандидаты, которые

    Senior SDE с опытом разработки драйверов и другого низкоуровневого кода, а так же с хорошим пониманием сетевых технологий (протоколы, драйвера, и т.п.)

    на собеседовании? Алексей, опровергните, верните мне веру в человеков!
    Т.е. я, не имея такового опыта, нахожусь в одном шаге от того,
    чтобы быть лучше 80% из “опытных”?

  30. Skip
    February 9th, 2011 at 03:14 | #30

    Только у Богдана пакет дочитывается до конца в случае, если буфер меньше длинны пакета? :)

  31. worm2
    February 9th, 2011 at 03:25 | #31

    @Bogdan
    Вы приняты :)

  32. Alexander Yastrebov
    February 9th, 2011 at 03:27 | #32

    1. Размер пакета big endian или little?
    2. Что делать, если не хватает размера буфера для приема пакета?

  33. ddiller
    February 9th, 2011 at 03:42 | #33

    char get_byte();
    size_t recv(char *buff, size_t size){
    if(!buff || size < 2){
    return 0; // invalid buffer
    }
    uint8_t first = get_byte();
    uint8_t second = get_byte();
    #if _LITTLE_ENDIAN_
    uint16_t header = first;
    header <<= 8;
    header |= second;
    #else
    uint16_t header = second;
    header < size){
    return 0; // invalid buffer
    }
    if(header < 2){
    return 0; // invalid header
    }
    char* curr = buff;
    *curr = first; curr++;
    *curr = second; curr++;
    for(size_t i = 0; i < header – 2; ++i, curr++){
    *curr = get_byte();
    }
    return header;
    }

  34. ddiller
    February 9th, 2011 at 03:45 | #34

    char get_byte();
    size_t recv(char *buff, size_t size){
      if(!buff || size < 2){
        return 0; // invalid buffer
      }
      uint8_t first = get_byte();
      uint8_t second = get_byte();
    #if _LITTLE_ENDIAN_
      uint16_t header = first;
      header <<= 8;
      header |= second;
    #else
      uint16_t header = second;
      header <<= 8;
      header |= first;
    #endif
      if(header > size){
        return 0; // invalid buffer
      }
      if(header < 2){
        return 0; // invalid header
      }
      char* curr = buff;
      *curr = first; curr++;
      *curr = second; curr++;
      for(size_t i = 0; i < header - 2; ++i, curr++){
        *curr = get_byte();
      }
      return header;
    }

    * This source code was highlighted with Source Code Highlighter.

    Фиг пойми как тут пофиксить чтобы видно было нормально.

  35. Vkni
    February 9th, 2011 at 03:54 | #35

    Тупой вопрос – какой endianness?

  36. worm2
    February 9th, 2011 at 04:25 | #36

    @Tyler
    Поскольку в описании протокола не указано, какой там должен быть endianness, а задача тестовая, значит, эту проблему можно вообще игнорировать.

  37. February 9th, 2011 at 04:34 | #37

    Те кто занимаются разработкой встраиваемых систем, такую задачу решают через день, точнее она уже давно решена.

    Как правильно заметили:

    1) здесь не указан порядок байт длины
    2) принимаемый пакет кладём во внутренний static unsigned char буфер на 65535 элементов (потому что 2 байта длина)
    3) что делать если длина равна 0 или 1? это ошибка пакета
    4) что делать если пользователь нам дал буфер недостаточной длины.

    Вообще алгоритмы которые закладываются на то, что все хорошо — неинтересные ибо тривиальные. Интереснее работать с алгоритмами, которые должны правильно обработать все ошибки.

  38. February 9th, 2011 at 04:36 | #38

    внимательно посмотрел все примеры кода выше в комментах. никто не догадался проверить, что длина пакета больше или равна 2м. наколка была именно в этом?

  39. R
    February 9th, 2011 at 05:02 | #39

    Алексей, если успешное решение этой задачки является необходимым требованием, то у вас плохо работает предварительный отбор претендентов…

    А вообще, я не вижу смысла издеваться над людьми, заставлять их писать длинный код на доске – вы там что, всё время на доске код пишете? Уж лучше дать компьютер и посмотреть, как они этот код будут писать в их любимом IDE или на худой конец в чём-нибудь блокнотоподобном – по их действиям сразу будет видно, что они знают, а с чем будут разбираться на ходу.

    Ну а по задачке, первым делом я бы спросил, как у вас называется функция (или макро) конвертации двух char в int, или в этой абстрактной задачке в вакууме такую функцию ещё никто не догадался сделать? :) Ну а остальное напишет любой кто хоть какое-то время занимался задачками с TopCoder, SPOJ и прочими – там как раз нужно написать небольшой код, чётко соответствующий поставленному условию, а если соревноваться то ещё и чем быстрее, тем лучше. Если вам такие люди нужны – так и напишите в вакансии.

  40. Vkni
    February 9th, 2011 at 05:44 | #40

    Кстати, как у вас проходит это “написание программы на доске”? Нужно писать непрерывным потоком или можно исправлять? Можно написать, потом проверить, а потом сказать – вот оно, смотрите?

  41. Michael
    February 9th, 2011 at 05:46 | #41

    Не указан endian заголовка

  42. Cpa3y
    February 9th, 2011 at 06:30 | #42

    size_t recv(char* buf, size_t size)
    {
    unsigned __int16 static len = 0;

    if (len)
    len = get_byte() | get_byte() < size)
    return 0;
    *((unsigned __int16*)buf) = len;

    len -=2;
    buf += 2;

    while (len–)
    *buf++ = get_byte();

    return *((unsigned __int16*)buf);
    }

  43. Pavel
    February 9th, 2011 at 07:10 | #43

    size_t recv(char* buf, size_t size)
    {
    // minimum buf size is 2 bytes for packet length
    if ( size < 2 )
    {
    return 0;
    }

    buf[0] = get_byte();
    buf[1] = get_byte();

    // assume packet length is big endian
    size_t const length = buf[1] || (buf[0] < size )
    {
    // buffer too small
    return 0;
    }

    // read packet data
    for (size_t i = 2; i != length; ++i)
    {
    buf[i] = get_byte();
    }

    return length;
    }

  44. Pavel
    February 9th, 2011 at 07:14 | #44

    @Pavel

    // assume packet length is big endian
    size_t const length = (buf[0] << 8) | buf[1];

  45. Vkni
    February 9th, 2011 at 07:17 | #45

    Ну вообще, конечно, круто – никто правильно не написал.

  46. Tyler
    February 9th, 2011 at 07:31 | #46

    > @Tyler функция не реентерабельна

    с чего ей быть реентерабельной? )
    в условии написано – “Функция recv вызывается на границе пакетов.”.
    как вызывающий достигнет этого – ни слова. то ли наша функция должна это делать, то ли будет еще одна go_next_packet().
    если не оговорено заранее, то логично писать более короткий вариант, не?

  47. Misha
    February 9th, 2011 at 07:37 | #47

    size_t recv(char* buf, size_t size)
    {
      unsigned short packSize = (get_byte() << 8)| get_byte();
      if (packSize > size)
      {
        return 0;
      }
      buf[0] = (char)(packSize >> 8);
      buf[1] = (char)packSize;
      unsigned short index = 2;
      while (packSize > index)
      {
        buf[index] = get_byte(3);
        index++;
      }
      return packSize;
    }

    * This source code was highlighted with Source Code Highlighter.

  48. dimayak
    February 9th, 2011 at 08:34 | #48

    size_t recv(char *buf, size_t size)
    {
    if (size < 2)
    return 0;
    char first_byte = get_byte();
    char second_byte = get_type();
    unsigned int head = first_byte < size)
    return 0;

    size_t i = 2;
    buf[0] = first_byte;
    buf[1] = second_byte;
    char byte;
    while (i <= head)
    {
    buf[i++] = get_byte();
    }
    return head;
    }

  49. dimayak
    February 9th, 2011 at 08:36 | #49

    Часть кода была обрезана

    size_t recv(char *buf, size_t size)
    {
    if (size < 2)
    return 0;
    char first_byte = get_byte();
    char second_byte = get_type();
    unsigned int head =
    first_byte < size)
    return 0;

    size_t i = 2;
    buf[0] = first_byte;
    buf[1] = second_byte;
    char byte;
    while (i <= head)
    {
    buf[i++] = get_byte();
    }
    return head;
    }

  50. Mikhail Edoshin
    February 9th, 2011 at 10:42 | #50

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

    #define HEADER_SIZE 2

    size_t
    recv(
    char *buf,
    size_t size)
    {
    char a, /* 1st size byte */
    b; /* 2nd size byte */
    size_t s, /* data size */
    S; /* total size */
    char *t; /* temporary buffer */
    int i; /* counter */

    /* Get size */
    a = get_byte();
    b = get_byte();
    s = a << 8 + b;
    S = s + HEADER_SIZE;
    /* See if data fits. */
    if (size < S)
    return 0;
    /* Get data. */
    buf[0] = a;
    buf[1] = b;
    for (i = HEADER_SIZE, i < S, i++)
    buf[i] = get_byte();
    return S;
    }

    Меня смущает, что get_byte() вроде как не ошибается, но пусть это будет так.

  51. Mikhail Edoshin
    February 9th, 2011 at 10:59 | #51

    А, ну да, ошибся, конечно :) Надо съедать пакет при ошибке и раз неизвестна endianness первых двух байтов, неясно, как их интерпретировать.

  52. Wesha
    February 9th, 2011 at 12:22 | #52

    Ребят, а чего вы все дружно накинулись на get_byte??? По одному байту из сокета сосать? Вы офигели, за такой оверхед расстреливать надо!

    read(sock, buf, 2) // получаем длину, причём read нам сразу должен дать правильную endiannes, ибо предполагаем, что посылающая платформа соответствует принимающей

    int packet_len = (int)((unsigned short int*)buf)[0]; // пусть компилятор сам разберётся, как он там инты хранит.

    if( size < packet_len ) return(0); // Та не лизе, батько! У криньку не лизе!

    if( packet_len < 2 ) return(0); // Если длина кривая – ругаемся

    int data_len = packet_len – 2;

    read(sock, buf + 2, data_len); // дочитываем что положено.

    return(packet_len);

    Обрамление read на случаи, если датаграмма не дошла целиком и прочитано меньше, чем попросили, писать не стал, ибо в лом.

  53. Alexander
    February 9th, 2011 at 17:25 | #53

    Точно такая же была у меня на собеседовании в британское подразделение ARM, которое кстати отличалось своей логичностью и правильностью в отличии от майкрософтовского собеседования, потому-что проверяли то точ надо и не было кучи brainfucker-ов, которые у меня активно спрашивали на интервью в Microsoft несмотря на все новые веяния, что их запрещено спрашивать.

    Правда в результате таки выбрал Microsoft :)

  54. February 9th, 2011 at 17:45 | #54

    @Wesha
    Менять условия задачи можно (с разумной монивацией), но это довольно скользкий путь. Например, приведённое решение содержит ошибку. Если предпологается, что соединение – это сокет (кстати, никто не говорил, что это сокет), то read может вернуть меньше байт чем запрошено.

  55. NiJazz
    February 11th, 2011 at 02:27 | #55

    Добрый день!
    Раз передающая сторона ведёт себя хорошо, значит, порядок байтов будет тоже правильным (это важно для вычисления размера пакета).
    Задача хранения излишних данных, не влезающих в буфер, не решена, условие об этом умалчивает. std::vector& на вход помог бы это обойти.
    Вот вариант решения
    size_t revc( char * buf, size_t size )
    {
    enum { header_size = 2 };
    assert( header_size <= sizeof( size_t ) );

    char * p = buf;
    size -= header_size;

    for ( int i = 0; i data_size ) ? data_size : size;
    left > 0;
    – left )
    {
    if ( !( *p++ = get_byte() ) )
    return 0;
    }

    return data_size;
    }

    PS Как форматировать код?

  56. NiJazz
    February 11th, 2011 at 02:35 | #56

    Вот ссылка на скриншот из текстового редактора:
    http://clip2net.com/s/LFRi

  57. proger
    February 13th, 2011 at 13:58 | #57

    char get_byte();

    size_t recv(char* buf, size_t size)
    {
    if (size < 2 || !buf)
    return 0;

    byte bCur;
    int nBytes = 0;
    bool blFullPack = false;
    while (blFullFack == false)
    {
    bCur = get_byte();
    nBytes++;
    if (nBytes = 2)
    blFullPack = MAKEDWORD(buf[0], buf[1]) == nBytes;

    }

    return nBytes;
    }

  58. Wesha
    February 14th, 2011 at 11:14 | #58

    @Алексей Пахунов
    Я ж специально написал — “Обрамление read на случаи, если датаграмма не дошла целиком и прочитано меньше, чем попросили, писать не стал, ибо в лом.” Я ж на работе, блин! Мне работать надо, а не задачки решать! ;)

  59. February 14th, 2011 at 11:48 | #59

    @Wesha

    А, пардон. Ну вот по этому это и скользский путь, хотя потенциально, может, и более выигрышный. Собеседуюший что-нибудь поймет не так и пошло-поехало.

  60. February 16th, 2011 at 07:31 | #60

    Не удержался от своих двух копеек:

    size_t recv(char* buf, size_t size)
    {
     if (size < 2)
        return 0;

     unsigned char const len1 = get_byte();
     unsigned char const len2 = get_byte();

     size_t const packet_len = // assuming network order
        static_cast <size_t> (len1) << 8 + static_cast <size_t> (len2);

     size_t const towrite = packet_len < size ? packet_len : size;

     buf[0] = len1;
     buf[1] = len2;

     for (size_t remaining = towrite - 2, i = 2; remaining > 0; --remaining, ++i)
     {
        buf[i] = get_byte();
     }

     return towrite;
    }

    * This source code was highlighted with Source Code Highlighter.

Comments are closed.