Отправка структуры с указателем на символ внутри? - PullRequest
3 голосов
/ 13 декабря 2010

я пытаюсь отправить пакет из моей C-программы в мою Delphi-программу, данные имеют переменный размер, и если я использовал char Data [1024]; он отправит 1024 байта, даже если данные имеют размер 3 байта, а если данные больше 1024, он не отправит их все: (

struct Packet
{
    int State_;
    char *Data;
};

struct Packet MyPacket;
MyPacket.Data = (char *) calloc(8, sizeof(char));
memcpy(MyPacket.Data, "thi sis", 8); 
send(Socket, MyPacket, (int)sizeof(struct Packet), 0);

спасибо О, кстати, я использую GCC под Windows

Ответы [ 7 ]

6 голосов
/ 14 декабря 2010

Старая хитрость для инкапсуляции пакета в одном блоке заключается в использовании массива длины 1 в конце пакета.

struct Packet 
{
    unsigned packetLengthInBytes_;
    /* All the fixed fields in the packet */
    int  State_;
    /* Generic data in the packet - of actual length given by packetDataLength(packet) */
    char Data[1];
};

unsigned packetDataLength(Packet* packet)
{
    return packet->packetLengthInBytes_ - (sizeof(Packet) - 1);
}

Packet* createPacketFromData(const char* data, unsigned dataSize)
{
    unsigned packetSize = sizeof(Packet) + dataSize - 1;
    Packet* packet = (Packet*)malloc(packetSize);
    packet->packetLengthInBytes_ = packetSize;
    memcpy(packet->Data, data, dataSize);
    return packet;
}

int sendData(int sock, const char* data, unsigned dataSize)
{
    Packet* packet = createPacketFromData(data, dataSize);
    /* [ Yes, think about endian issues.] */
    send(sock, packet, packet->packetLengthInBytes_, 0);
    free(packet);
}

Обратите внимание, что это означает, что у нас есть один вызов send (), и в общем случае мы можем передавать пакет как один объект с одним вызовом выделения и одним вызовом освобождения.

5 голосов
/ 13 декабря 2010

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

1 голос
/ 13 декабря 2010

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

struct Packet {
    int State_;
    char *Data;
};
struct PacketHeader {
    uint32_t state;
    uint32_t len;
};

int send_packet(int sock, struct Packet *pkt)
{
    struct PacketHeader hdr;
    int len = strlen(pkt->Data); /* If Data is a C string */
    hdr.state = htonl(pkt->State_);
    hdr.len = htonl(len);
    send(sock, &hdr, sizeof(hdr), 0);
    send(sock, pkt->data, len, 0);
}

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

1 голос
/ 13 декабря 2010

Ваш код неверен - когда вы отправляете структуру напрямую, отправляется указатель (адрес Data_), а не действительная строка.У вас есть два варианта:

  1. Как вы уже упоминали, используйте массив фиксированного размера внутри структуры.

  2. Вручную отправьте длину, а затемфактические строковые данные через сокет, например:


int length = 8;
char *data = (char *) calloc(length, sizeof(char));
memcpy(data, "thi sis", 8); 
send(Socket, &length, sizeof(length), 0);
send(Socket, data, length, 0);
1 голос
/ 13 декабря 2010

Указатель является ссылкой на адрес памяти, поэтому вы в настоящее время отправляете адрес вместо содержимого.Таким образом, вам нужно изменить его для отправки массива фактических символов.
Вам необходимо отправить либо информацию о размере (предпочтительно) перед фактическим массивом символов, либо определить знак конца символьного массива

1 голос
/ 13 декабря 2010

Принимающая сторона (Delphi) должна быть осведомлена о размере данных с помощью метода, согласованного отправителем и получателем (т. Е. Вам следует внедрить простой протокол для передачи данных).Поскольку вы передаете весь struct, ваш протокол также должен позаботиться об указании типов.Самое простое решение - использовать формат обмена текстовыми данными, такой как XML или JASON:

// Code to demonstrate the idea, may not compile.
struct Packet MyPacket;
MyPacket.State_ = 0;
MyPacket.Data = (char *) calloc(8, sizeof(char));
memcpy(MyPacket.Data, "thi sis", 8); 
const char* packetXml = PacketToXml();
/*
packetXml = 
<Packet>
<State>0</State>
<Data>thi sis</Data>
</Packet>
*/
size_t len = strlen(packetXml);
send(Socket, (char*)&len, sizeof(size_t), 0);
send(Socket, packetXml, len, 0);
0 голосов
/ 13 декабря 2010

Вам необходимо указать правильную длину отправляемых данных. Использование:

sizeof(struct Packet)

, как вы сделали, неправильно и даст вам только 8 байтов, 4 байта для int и 4 байта для char * в 32-битной системе.

Обратите внимание, что

char*

отличается от типа символьного массива. Это указатель на символ. Например, результаты sizeof здесь разные:

char arr[1024];
char* pc = arr;
printf("%d, %d", sizeof(pc), sizeof(arr));
...