第一次弄不定長包,大家都不太熟,網絡上又衆說紛紜,沒有一個完整代碼可以參考,所以我們走了不少的彎路。
昨天把解壓包模塊測試一遍,並進行了修訂(主要是隊員們很迫切的再問這個模塊到底要怎麼弄,不然我可能今天還在計劃)。
看,這個模塊,19號就寫好了,一直拖到12號才測。
接下來來看一下對比代碼:
關於原先解壓包模塊的設計圖和代碼在日報2中:FTP文件管理項目(本地雲)項目日報(二)
但是我要重新做個圖了。
舊圖:
新圖:
新舊對比
1、舊圖中使用的是兩個包對象完成一條服務,但是新包是一個對象,爲什麼呢?因爲之前我一直以爲包的body一旦分配內存之後,便固定住了,後來我發現,可以有以下操作:
char *a;
a = new char[10];
sprintf(a,"abcdefg");
a = new char[20];
sprintf(a,"abcdefghijklmn");
而上下兩塊內存是不衝突的,頂多是一點內存碎片,如果嚴謹一點,那就delete之後再new。
受到啓發,我將代碼進行修改,在解包時使用一塊不算大的內存(包體通常很小,大的是包頭),封包的時候再根據實際情況進行封包。
2、解包、封包時包體的偏移量。
之前用定長包的時候,對包的偏移就是sizeof(body),因爲那時候body是這樣定義的:char body[1024 - sizeof(head) - sizeof(tail)]
,所以sizeof(body)並沒有什麼問題,但是現在的body是不定長的,是這樣的:char* body; body = new char[sizeof(具體協議包)]
,那這時候情況就不一樣了,沒什麼特殊情況,指針大小都是4。關於這點,我調試了好一會兒纔想起來,爲什麼數據打印出來會丟哈哈哈,原來就寫了四個字節進去。
接着看一下代碼修改:
解壓包舊代碼:
bool PacketBase::pack()
{
Body = new char[Body_Size];
memcpy(m_Data, &this->Head, sizeof(packet_header_t));
memcpy(m_Data + sizeof(packet_header_t), this->Body, sizeof(Body)); //這個Body長度在封包的時候定
memcpy(m_Data + sizeof(packet_header_t) + sizeof(Body), &this->Tail, sizeof(packet_tali_t));
return true;
}
bool PacketBase::unpack()
{
if (Body_Size <= 0) { //如果數據不足
std::cout << "數據包破損" << std::endl;
return false;
}
Body = new char[Body_Size]; //防止越界可以在這裏下手
memcpy(&this->Head, m_Data, sizeof(packet_header_t)); //先將包頭讀出
memcpy(Body, m_Data + sizeof(packet_header_t), sizeof(Body));
memcpy(&this->Tail, m_Data + sizeof(packet_header_t) + sizeof(Body), sizeof(packet_tali_t));
return true;
}
解壓包新代碼:
bool PacketBase::pack()
{
memcpy(m_Data, &this->Head, sizeof(packet_header_t));
memcpy(m_Data + sizeof(packet_header_t), this->Body, Body_Size);
memcpy(m_Data + sizeof(packet_header_t) + Body_Size, &this->Tail, sizeof(packet_tali_t));
return true;
}
bool PacketBase::unpack()
{
if (Body_Size <= 0) {
std::cout << "數據包破損" << std::endl;
return false;
}
Body = new char[Body_Size];
memcpy(&this->Head, m_Data, sizeof(packet_header_t)); //ÏȽ«°üÍ·¶Á³ö
memcpy(Body, m_Data + sizeof(packet_header_t), Body_Size);
memcpy(&this->Tail, m_Data + sizeof(packet_header_t) + Body_Size, sizeof(packet_tali_t));
return true;
}
注意加以區分。
具體協議包就代碼:
bool PacketCommand1::replyLogin(int state, int dir_id,int fd) {
Head.funcId = 0x01;
Head.optid = 0x01;
Head.fd = fd;
Head.usrlenth = sizeof(Body); //誰開的包誰封回去,所以大小是相對靜止了
Head.syn = 0x04;
res_login_t* body = (res_login_t*)Body;
body->login_ret = state;
body->dir_id = dir_id;
Tail.pack_tail = 0x05;
return this->pack();
}
具體協議包新代碼:
bool PacketCommand1::replyLogin(int state, int dir_id,int fd) {
//加了下面這幾行
int sz = sizeof(res_login_t);
this->setBodySize(sz);
this->Body = new char[sz];
Head.funcId = 0x01;
Head.optid = 0x01;
Head.fd = fd;
Head.usrlenth = sz; //還有這裏
Head.syn = 0x04;
res_login_t* body = (res_login_t*)Body;
body->login_ret = state;
body->dir_id = dir_id;
Tail.pack_tail = 0x05;
return this->pack();
}
然後呢,放一個登陸包的測試代碼吧,因爲每個包的實現方式都一樣:
測試代碼:
#include"PacketCommand1.h"
#include<iostream>
using namespace std;
int main()
{
PacketCommand1* pack1 = new PacketCommand1();
pack1->replyLogin(7, 9, 6);
pack1->unpack();
cout << pack1->getHead()->fd << endl;
res_login_t *body = (res_login_t*)pack1->getBody();
cout << body->dir_id << " " << body->login_ret << endl;
}