От новичка до гуру: Курсы программирования на CyberDuff

Отправить пакет с 32-битной и получить на 64-битной машине

Я пишу программу, которая обменивается сообщениями между разными битовыми машинами. Мои функции отправки, получения и чтения выглядят так, как показано ниже. Проблема в том, что у меня есть формат пакета, в котором я отправляю сообщения. Тип пакета определяет тип сообщения, например, «Добро пожаловать», «Данные», «Имя пользователя» и т. д. Я читаю тип пакета с SD, а затем читаю оставшиеся данные. Когда я отправляю из 32-битного, тип пакета будет от x до x+4, а данные будут от x+4 до так далее. При приеме на 64-битной машине тип пакета читается от x до x+8 и x+8 в качестве данных, поэтому моя функция чтения ждет вечно, чтобы получить оставшиеся байты длины, как указано. Как исправить эту проблему в этом коде?

/*----------------------------------------------------------------*/

/* messsges received/sent by server */
#define WELCOME_MSG    0
#define USER_NAME     1
#define EMAIL_MSG_TO_SERVER    2
#define EMAIL_MSG_TO_CLIENT 3
#define CLOSE_CON 4
/* structure of a packet */
typedef struct _packet {

    /* packet type */
    char      type;

    /* packet length */
    long      lent;

    /* packet text */
    char *    text;

} Packet;

int readn(int sd, char *buf, size_t n) {
    printf("readn via utils. %d, %s, %d\n", sd, buf, n);
    size_t toberead = n;
    char * ptr = buf;

    while (toberead > 0) {

        int errno_save = 0;

        fprintf(stderr, "toberead: %zu\n", toberead);

        ssize_t  byteread = read(sd, ptr, toberead);
        errno_save = errno;

        fprintf(stderr, "toberead: %zu, byteread: %zd\n", toberead, byteread);

        if (byteread <= 0) {
            fprintf(stderr, "byteread val: %d",byteread);
            if (byteread == -1)
            {
                perror("read");
                errno = errno_save;
            }
            return (0);
        }

        toberead -= byteread;
        ptr += byteread;
    }

    if ('\0' != buf[n]) /* This assumes buf is one byte **larger** then n. */
    {
        buf[n] = '\0';
    }

    fprintf(stderr, "Finished readn. %s\n", buf);
    return (1);
}

/*----------------------------------------------------------------*/



Packet *recvpkt(int sd)
{
    printf("Recvpkt via utils.\n");
    Packet *pkt;

    /* allocate space for the pkt */
    pkt = (Packet *) calloc(1, sizeof(Packet));
    if (!pkt) {
        fprintf(stderr, "error : unable to calloc\n");
        return(NULL);
    }

    /* read the message type */
    if (!readn(sd, (char *) &pkt->type, sizeof(pkt->type))) {
        free(pkt);
        return(NULL);
    }

    /* read the message length */
    if (!readn(sd, (char *) &pkt->lent, sizeof(pkt->lent))) {
        free(pkt);
        return(NULL);
    }
    pkt->lent = ntohl(pkt->lent);

    /* allocate space for message text */
    if (pkt->lent > 0) {
        pkt->text = (char *) malloc(pkt->lent);
        if (!pkt) {
            fprintf(stderr, "error : unable to malloc\n");
            return(NULL);
        }

        /* read the message text */
        if (!readn(sd, pkt->text, pkt->lent)) {
            freepkt(pkt);
            return(NULL);
        }
    }

    fprintf(stderr, "Reading packet complete succesfully.\n");

    /* done reading */
    return(pkt);
}

int sendpkt(int sd, char typ, long len, char *buf)
{
    fprintf(stderr, "Send packet via utils. sd: %d, typ: %c, len: %lu, buf: %s\n", sd, typ, len, buf);
    char tmp[8];
    long siz;

    /* write type and lent */
    bcopy(&typ, tmp, sizeof(typ));
    siz = htonl(len);
    bcopy((char *) &siz, tmp+sizeof(typ), sizeof(len));
    write(sd, tmp, sizeof(typ) + sizeof(len));

    /* write message text */
    if (len > 0)
        write(sd, buf, len);
    return(1);
}

void freepkt(Packet *pkt)
{
    fprintf(stderr, "Freeing packet.\n");
    free(pkt->text);
    free(pkt);
} 
01.12.2013

  • Никогда не используйте int или long в пакетах или подобных данных. Вот для чего нужны uint32_t и друзья. 01.12.2013
  • Никогда не используйте structs в качестве сетевых протоколов. Вы только что обнаружили одну причину, и есть около пяти других. Создайте себе проводной протокол, определенный в октетах, и напишите код для его отправки и получения. 02.12.2013
  • Как говорит EJP, в основном.. 02.12.2013
  • Нет ничего плохого в использовании структур для сетевых протоколов, вы просто должны убедиться, что они используют одинаковое выравнивание данных на обоих концах, используя прямые операторы #pragma или кросс-платформенные файлы заголовков pshpack.... 02.12.2013

Ответы:


1

Используйте заголовок stdint.h, который определяет типы с именами, указывающими на их размер. Они всегда будут такого размера на любой платформе.

#include <stdint.h>

/* structure of a packet */
typedef struct _packet {
    /* packet type */
    uint8_t  type;
    /* packet length */
    uint32_t lent;
    /* packet text */
    char *   text;
} Packet;
01.12.2013
  • Это помогло. Большое спасибо. 01.12.2013
  • Типы данных — это только половина проблемы. Выравнивание данных — это вторая половина. Если вы отправляете структуры через сокеты, вы должны использовать не только одни и те же типы данных, но и одинаковое выравнивание структур. Если вы не отправляете каждый элемент структуры по отдельности, выравнивание не имеет значения. 02.12.2013

  • 2

    Вам необходимо согласовать общий двоичный формат, которого придерживаются все стороны. Я подозреваю, что вы знаете об этом, но не понимаете, что некоторые типы имеют разные размеры на разных машинах. Вы, вероятно, работаете с машинами, для которых длина имеет разные размеры. И вы объявили, что ваши данные длины имеют тип long. Так что на некоторых машинах это 32 бита, а на других 64 бита.

    Вероятно, вам следует решить использовать 32-битное целое число. Вам нужно использовать тип, размер которого не отличается на разных машинах. Например int32_t.

    01.12.2013
  • Я знал о int32_t и т. д., но никогда не осознавал их важности после трех лет исправления ошибок в C++. Раньше я не хотел этого понимать, пока сам не написал код. Спасибо за помощь мне. 01.12.2013
  • Я смог сразу понять решение, увидев код, но ваше объяснение дало мне больше знаний. 01.12.2013
  • Новые материалы

    Основы Spring: Bean-компоненты, контейнер и внедрение зависимостей
    Как лего может помочь нашему пониманию Когда мы начинаем использовать Spring, нам бросают много терминов, и может быть трудно понять, что они все означают. Итак, мы разберем основы и будем..

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

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

    Игра в прятки с данными
    Игра в прятки с данными Я хотел бы, чтобы вы сделали мне одолжение и ответили на следующие вопросы. Гуглить можно в любое время, здесь никто не забивается. Сколько регионов в Гане? А как..

    «Раскрытие математических рассуждений с помощью Microsoft MathPrompter и моделей больших языков»
    TL;DR: MathPrompter от Microsoft показывает, как использовать математические рассуждения с большими языковыми моделями; 4-этапный процесс для улучшения доверия и рассуждений в математических..

    Раскройте свой потенциал в области разработки мобильных приложений: Абсолютная бесплатная серия
    Глава 6: Работа в сети и выборка данных Глава 1: Введение в React Native Глава 2: Основы React Native Глава 3: Создание пользовательского интерфейса с помощью React Native Глава 4:..

    Все о кейсах: Camel, Snake, Kebab & Pascal
    В программировании вы сталкивались с ними при именовании переменной, класса или функции. Поддержание согласованности типов и стилей случаев делает ваш код более читабельным и облегчает совместную..