2010-12-13 53 views
3

即時嘗試從我的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); 

感謝 windows下哦,順便說一句,即時通訊使用gcc

+0

**問題**是? 「爲什麼這不起作用?」或者是其他東西? – 2010-12-13 08:10:11

回答

5

將數據包封裝在一個塊中的舊技巧是在數據包末尾使用長度爲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()調用,一般都能通過左右一包作爲一個對象,一個分配調用和一個釋放呼叫。

+0

感謝:D,它工作得很好,只需要對gcc進行一些修改:D – killercode 2010-12-14 13:24:05

1

接收方(德爾福)應該通過發送方和接收方達成一致的方法來了解數據的大小(例如,您應該實現一個簡單的數據傳輸協議)當您傳送整個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

是的,但我嗅探發送的數據,它是8字節,不管我如何更改數據內容 – killercode 2010-12-13 05:25:34

+0

@ killercode請參閱更新的答案。 – 2010-12-13 05:30:45

+0

我想發送一個數據包,以避免在delphi端收到的數據的手動解析,也可以向數據包添加更多的元素。 – killercode 2010-12-13 05:45:46

1

指針是對內存地址的引用,所以您當前正在發送地址而不是內容。因此您需要將其更改爲發送實際字符數組。
您需要在實際字符數組之前發送大小信息(首選)或定義字符數組符號的末尾

0

您需要指定發送的數據的正確長度。使用:

sizeof(struct Packet) 

爲你所做的是錯的,只會給你8個字節,4個字節int和4個字節的字符*,在32位系統上。

注意

char* 

是從字符陣列不同的類型。這是一個字符的指針。例如,這裏的sizeof的結果是不同的:

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

我確實已經使用sizeof(struct Packet) – killercode 2010-12-13 05:44:08

+0

@killercode:上面可能沒有清楚,但它確實提到了你使用它的方式是錯誤的。 – sashang 2010-12-13 05:58:06

1

你的代碼是不正確 - 當你直接將結構類似的,你要發送一個指針(的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

我還會補充說,發送'int'是一個壞主意....使用類似於'uint32_t'的typedefs,不要忘記調用像'htonl'這樣的函數來適應不同的字節順序。 – asveikau 2010-12-13 05:41:05

+0

是的,但如果我需要更多元素呢?用戶狀態,帳戶狀態等... Packet結構中的更多元素我的意思是? – killercode 2010-12-13 05:46:48

+0

@asveikau:你對此絕對正確。 – casablanca 2010-12-13 15:18:45

5

你應該實現你的結構序列化方法。 你現在試圖發送它的方式永遠不會起作用,因爲你實際上並沒有發送你的char *的內容,你正在發送指針本身。這只是一個內部地址,接收程序將不知道你要完成什麼。

1

真的,你想要做的是發送一個短的標題爲您的消息後面的可變長度數據。標題至少應包含後面數據的大小。一個簡單的實現是這樣的:

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); 
} 

我不知道德爾福,但解析包,你做的正好相反。讀取頭文件以獲取數據大小,然後從套接字中讀取大量數據到新分配的緩衝區中。