http://www.cnblogs.com/my_life/articles/4943353.html
http://www.cppblog.com/tonykee/archive/2008/02/17/42829.aspx
http://www.cnblogs.com/foohack/p/4718320.html
網(wǎng)絡(luò)數(shù)據(jù)傳輸,可以直接發(fā)送字符串,但不能直接發(fā)送一個(gè)結(jié)構(gòu)體。
網(wǎng)絡(luò)上傳輸數(shù)據(jù),因?yàn)榘l(fā)送端和接收端,通常不能保證是兩邊是相同的編程語(yǔ)言,就算都是使用C語(yǔ)言,CPU字節(jié)序,或者CPU位數(shù)不一樣,直接將結(jié)構(gòu)體的數(shù)據(jù)整理成流發(fā)送過(guò)去,數(shù)據(jù)排序或者長(zhǎng)度會(huì)跟你想象的不一樣。解釋起來(lái)比較費(fèi)篇幅。
這里說(shuō)下通常的解決辦法:
http://www.cnblogs.com/kaijia9/p/3394953.html
UDP傳輸模式是數(shù)據(jù)報(bào),TCP傳輸模式為字節(jié)流,字節(jié)流與數(shù)據(jù)報(bào)區(qū)別在于有邊界與無(wú)邊界。例如:TCP客戶端發(fā)送了三個(gè)數(shù)據(jù)包,開(kāi)的緩存足夠大服務(wù)端一次可接收三個(gè)數(shù)據(jù)包的數(shù)據(jù),這就是無(wú)邊界。UDP客戶端發(fā)送了三個(gè)數(shù)據(jù)包,就算開(kāi)的緩存足夠大服務(wù)端一次也只能接收一個(gè)數(shù)據(jù)包,這就是有邊界。
還有就是協(xié)議會(huì)維護(hù)源地址和目的地址直到協(xié)議要求斷開(kāi)連接,這就決定了TCP不能進(jìn)行廣播和多播。
直接發(fā)送結(jié)構(gòu)體的方式【是不對(duì)的】:
· char send_buf[1024] = "tony 2000 ";
· memset(send_buf,0,1024);
· struct msg
· {
· int cmd;
· int sendID;
· int recvID;
· string name;
· int number;
· };
· msg msg1;
· msg1.cmd = COMMAND;
· msg1.sendID = 2120100324;
· msg1.recvID = 2120100325;
· msg1.name = "Tony";
· msg1.number = 2000;
·
· //memcpy(send_buf,&msg1,sizeof(msg));
· //int len_send = send(Socket,send_buf,sizeof(send_buf),0);
· int len_send = send(Socket,(char *)&msg1,sizeof(msg),0);
如上所示,
TCP是無(wú)邊界的字節(jié)流傳輸,所以需要將結(jié)構(gòu)體轉(zhuǎn)換為字符串后在發(fā)送,最后三行用了兩種方法發(fā)送屬于結(jié)構(gòu)體類型的數(shù)據(jù),通過(guò)TCP傳輸。最后在接收方需要轉(zhuǎn)換為結(jié)構(gòu)體。
紅色: 數(shù)組屬于字符串,該方法是將要發(fā)送結(jié)構(gòu)體所占字節(jié)大小考到數(shù)組中, 再通過(guò)數(shù)組發(fā)送。
藍(lán)色: 將該結(jié)構(gòu)體地址轉(zhuǎn)化為char* 類型的地址,目的是使該指針加1移動(dòng)時(shí) 是按一個(gè)字節(jié)移動(dòng),而不是加1按該結(jié)構(gòu)體大小移動(dòng),然后發(fā)送該結(jié)構(gòu) 體所占字節(jié)大小。
struct AP_INFO {
char action;
char *test;
char *testlist;
};
這樣定義的結(jié)構(gòu)更是不能直接傳輸?shù)模?span style="color: #ff0000;">因?yàn)槠渲杏袃蓚€(gè)是指針,指向的是結(jié)構(gòu)以外的內(nèi)存地址,通過(guò)socket傳輸?shù)綄?duì)方機(jī)器后無(wú)法解析,搞得不好的話甚至可能造成程序崩潰!
原始的序列化:將結(jié)構(gòu)體的成員一個(gè)一個(gè)的復(fù)制到內(nèi)存再發(fā)到服務(wù)端
===============================================
http://www.cnblogs.com/foohack/p/4718320.html
大家都知道,在進(jìn)行網(wǎng)絡(luò)傳輸?shù)臅r(shí)候,因?yàn)榉植荚诰W(wǎng)絡(luò)上的每臺(tái)機(jī)器可能大小端的不同,需要進(jìn)行字節(jié)序列轉(zhuǎn)換,比如用win32 API的socket里面就有類似與htonl等與此類似的函數(shù),它就是把主機(jī)端的字節(jié)序列轉(zhuǎn)換成網(wǎng)絡(luò)傳輸?shù)淖止?jié)序列。當(dāng)然也有與之相反的函數(shù)ntohl,是把網(wǎng)絡(luò)字節(jié)序,轉(zhuǎn)換為主機(jī)字節(jié)序。
比如 int data = 0x32461256在小端機(jī)器上按照“高高低低”的原則,內(nèi)存上是這樣表示,0x56,0x12,0x46,0x32。進(jìn)行htonl轉(zhuǎn)換后,在內(nèi)存中的布局就會(huì)變成0x32,0x46,0x12,0x56。
所以,我們通過(guò)socket的send發(fā)送結(jié)構(gòu)體或者對(duì)象的時(shí)候要注意了,需要序列化,當(dāng)然,大家可以說(shuō),這樣一個(gè)結(jié)構(gòu)體那么多字段都要手動(dòng)用htonl之類的函數(shù)序列化,那么太麻煩,其實(shí)網(wǎng)絡(luò)上有專門的序列化庫(kù),比如google的protobuff,boost也有相應(yīng)模塊,Qt的QDataStream內(nèi)部就實(shí)現(xiàn)了序列化,序列化實(shí)際上就是把大小端,還有結(jié)構(gòu)體字節(jié)對(duì)齊等細(xì)節(jié)屏蔽了。所以,一般通過(guò)send發(fā)送結(jié)構(gòu)體不能直接把它轉(zhuǎn)換成char*的字節(jié)序列發(fā)送,在發(fā)送之前,要先做序列化。
以下給出用Qt的QDataStream做序列化例子:
http://www.java2s.com/Code/Cpp/Qt/SerializationwithQDataStream.htm
http://comments.gmane.org/gmane.comp.lib.qt.general/38559
注意:char型的數(shù)據(jù)是不用序列化的,因?yàn)橹皇菃蝹€(gè)字節(jié),不是多字節(jié)占用
references:
http://stackoverflow.com/questions/5894622/sending-any-structure-via-qtcpsocket
http://stackoverflow.com/questions/2473300/overloading-the-qdatastream-and-operators-for-a-user-defined-type
http://stackoverflow.com/questions/1577161/passing-a-structure-through-sockets-in-c
http://stackoverflow.com/questions/17817280/send-struct-over-socket-in-c
=======================
http://hcq0618.blog.163.com/blog/static/1780903512013101831120514/
主要技術(shù)問(wèn)題:windows,linux等系統(tǒng)采用LITTLE_ENDIAN字節(jié)序,而java自身采用BIG_ENGIAN字節(jié)序,BIG_ENGIAN是指低地址存放最高有效字節(jié)(MSB),而LITTLE_ENDIAN則是低地址存放最低有效字節(jié)。Java程序?qū)懙目蛻舫绦蚨送琧++的服務(wù)端程序交互時(shí)結(jié)構(gòu)體的某些數(shù)據(jù)類型需要轉(zhuǎn)換字節(jié)序。本文解決方法,java客戶端程序發(fā)送數(shù)據(jù)時(shí)做相應(yīng)的轉(zhuǎn)換字節(jié)序,等收到數(shù)據(jù)時(shí)再做一次字節(jié)序的轉(zhuǎn)換。
現(xiàn)在的網(wǎng)絡(luò)程序多數(shù)采用可靠交付的TCP協(xié)議,其采用字節(jié)流的傳輸方式,c++程序中用結(jié)構(gòu)體來(lái)模擬報(bào)頭以此界定每次發(fā)送的報(bào)文。所以網(wǎng)絡(luò)中整個(gè)字節(jié)流的格式:報(bào)頭+數(shù)據(jù)負(fù)載+報(bào)頭+數(shù)據(jù)負(fù)載……
c++與java進(jìn)行socket通信時(shí)注意事項(xiàng)
因?yàn)?span style="color: #ff0000;">java發(fā)送的都是網(wǎng)絡(luò)字節(jié)序(big-endium),而c++是主機(jī)字節(jié)序(little-endium),所以當(dāng)消息中有整型,浮點(diǎn)型(應(yīng)盡量避免使用)的時(shí)候需要用htonl,htons,ntohl,ntohs等函數(shù)轉(zhuǎn)換一下,字符串由于是單字節(jié)排序的不需要轉(zhuǎn)換,但應(yīng)注意c++字符串是以'/0'作為結(jié)束符的,如果找不到'/0'可能會(huì)出現(xiàn)一些亂碼,所以接收的時(shí)候可以分配一個(gè)length+1的buffer用來(lái)接收消息(貌似java會(huì)自動(dòng)處理).
網(wǎng)絡(luò)只有字節(jié)的概念,所以,你必須把你要傳送的東西全部轉(zhuǎn)換成字節(jié)后,再發(fā)送出去,在c中,以字節(jié)方式存在的數(shù)據(jù),是不需要進(jìn)行轉(zhuǎn)換的,比如char *什么的
可參考qiyi的ChatMsg.h中的序列化函數(shù):對(duì)單字節(jié)字段【char, char *, char [], 可當(dāng)作char*的string等】都沒(méi)做大小端轉(zhuǎn)換,對(duì)其他的short, int, long等都需要大小端轉(zhuǎn)換
short 或者 long的數(shù)據(jù)在進(jìn)行通信的時(shí)候最好養(yǎng)成:
1、發(fā)送的時(shí)候使用:htons(l)
2、接受的時(shí)候使用:ntohs(l)
當(dāng)然了一般我都不用int型的數(shù)據(jù)通信,從來(lái)都是字符串通信,發(fā)送方利用sprintf組織,接收方利用atoi進(jìn)行轉(zhuǎn)換~~
http://beginman.cn/unp/2015/10/15/unp-socket/
在《Linux高性能服務(wù)器編程》中這里理解更好些:
現(xiàn)代PC大多采用小端字節(jié)序,因此小端字節(jié)序又稱為主機(jī)字節(jié)序。
當(dāng)格式化的數(shù)據(jù)(比如32 bit整型數(shù)和16 bit短整型數(shù))在兩臺(tái)使用不同字節(jié)序的主機(jī)之間直接傳遞時(shí),接收端必然錯(cuò)誤地解釋之。解決問(wèn)題的方法是:發(fā)送端總是把要發(fā)送的數(shù)據(jù)轉(zhuǎn)化成大端字節(jié)序數(shù)據(jù)后再發(fā)送,而接收端知道對(duì)方傳送過(guò)來(lái)的數(shù)據(jù)總是采用大端字節(jié)序【因?yàn)榫W(wǎng)絡(luò)序是大端】,所以接收端可以根據(jù)自身采用的字節(jié)序決定是否對(duì)接收到的數(shù)據(jù)進(jìn)行轉(zhuǎn)換(小端機(jī)轉(zhuǎn)換,大端機(jī)不轉(zhuǎn)換)。因此大端字節(jié)序也稱為網(wǎng)絡(luò)字節(jié)序,它給所有接收數(shù)據(jù)的主機(jī)提供了一個(gè)正確解釋收到的格式化數(shù)據(jù)的保證。
知道為什么有模式的存在,下面需要了解應(yīng)用場(chǎng)景:
1、不同端模式的處理器進(jìn)行數(shù)據(jù)傳遞時(shí)必須要考慮端模式的不同
2、在網(wǎng)絡(luò)上傳輸數(shù)據(jù)時(shí),由于數(shù)據(jù)傳輸?shù)膬啥藢?duì)應(yīng)不同的硬件平臺(tái),采用的存儲(chǔ)字節(jié)順序可能不一致。所以在TCP/IP協(xié)議規(guī)定了在網(wǎng)絡(luò)上必須采用網(wǎng)絡(luò)字節(jié)順序,也就是大端模式, 對(duì)于char型數(shù)據(jù)只占一個(gè)字節(jié),無(wú)所謂大端和小端。而對(duì)于非char類型數(shù)據(jù),必須在數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上之前將其轉(zhuǎn)換成大端模式。接收網(wǎng)絡(luò)數(shù)據(jù)時(shí)按符合接受主機(jī)的環(huán)境接收。
struct { char one; //字符 unsigned short two; //short類型 unsigned int three; //int類型 char * four; //字符串}BinaryProtocolStruct;
//one為char類型不需要進(jìn)行網(wǎng)絡(luò)主機(jī)傳輸模式轉(zhuǎn)換,把one的值寫(xiě)入到內(nèi)存塊中
//two為unsigned short 類型,所以要進(jìn)行網(wǎng)絡(luò)主機(jī)的傳輸字節(jié)順序的轉(zhuǎn)換 htons
//three 為int類型 所以要進(jìn)行網(wǎng)絡(luò)主機(jī)的傳輸字節(jié)順序的轉(zhuǎn)換 htonl
//four為字符串不需要進(jìn)行存儲(chǔ)轉(zhuǎn)換
主機(jī)字節(jié)序與網(wǎng)絡(luò)字節(jié)序
主機(jī)字節(jié)序
不同的CPU有不同的字節(jié)序類型 這些字節(jié)序是指整數(shù)在內(nèi)存中保存的順序 這個(gè)叫做主機(jī)序
最常見(jiàn)的有兩種
1. Little endian:將低序字節(jié)存儲(chǔ)在起始地址
2. Big endian:將高序字節(jié)存儲(chǔ)在起始地址
網(wǎng)絡(luò)字節(jié)序
網(wǎng)絡(luò)字節(jié)順序是TCP/IP中規(guī)定好的一種數(shù)據(jù)表示格式,它與具體的CPU類型、操作系統(tǒng)等無(wú)關(guān),從而可以保證數(shù)據(jù)在不同主機(jī)之間傳輸時(shí)能夠被正確解釋。網(wǎng)絡(luò)字節(jié)順序采用big endian排序方式。
同樣 在網(wǎng)絡(luò)程序開(kāi)發(fā)時(shí) 或是跨平臺(tái)開(kāi)發(fā)時(shí) 也應(yīng)該注意保證只用一種字節(jié)序 不然兩方的解釋不一樣就會(huì)產(chǎn)生bug.
Java 程序與 C++ 之間的 SOCKET 通訊
4.1 字節(jié)序問(wèn)題
一直以來(lái)都在進(jìn)行著C++ 上面的網(wǎng)絡(luò)開(kāi)發(fā),發(fā)現(xiàn)在 C++ 上面進(jìn)行通訊的時(shí)候,基本上都沒(méi)有考慮到網(wǎng)絡(luò)字節(jié)序的問(wèn)題,特別是網(wǎng)絡(luò)應(yīng)用中的用戶數(shù)據(jù)。大家都知道網(wǎng)絡(luò)通訊傳輸?shù)亩际亲止?jié)流的數(shù)據(jù),于是都是定義一個(gè) char 類型的緩沖區(qū),然后不管 int , WORD, DWORD 還是其他自定義類型的結(jié)構(gòu)對(duì)象也好,都直接 memcpy() 拷貝到緩沖區(qū),直接發(fā)送出去了,根本都沒(méi)有考慮所傳輸數(shù)據(jù)的網(wǎng)絡(luò)字節(jié)序問(wèn)題。如果非要說(shuō)一點(diǎn)關(guān)注了網(wǎng)絡(luò)字節(jié)序問(wèn)題的話,那就是有一個(gè)地方,大家都回去用到的,也就是網(wǎng)絡(luò)通訊端口,把端口號(hào)賦值給 sockaddr_in .sin_port之時(shí)大家都會(huì)使用了htons() ,也就是把端口號(hào)從主機(jī)字節(jié)序轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序。
因?yàn)檫@些程序的服務(wù)器端也好,客戶端也好,都是在x86 系列系統(tǒng)下運(yùn)行,并且都是 C++ 編譯出來(lái)的,所以不會(huì)因?yàn)樽止?jié)序而出現(xiàn)問(wèn)題。
現(xiàn)在所做項(xiàng)目,涉及到Java 與 C++ 之間的 SOCKET 通訊,這樣的情況下,就需要大家都按規(guī)則來(lái)辦事了, C++ 方面?zhèn)鬏數(shù)亩嘧止?jié)類型數(shù)據(jù)還請(qǐng)從主機(jī)字節(jié)序轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序再進(jìn)行傳輸。
當(dāng)然,數(shù)據(jù)是由程序員來(lái)組合的,也是由程序員來(lái)解析的,所以如果不按標(biāo)準(zhǔn)行事也是可以的。但是就需要在解析數(shù)據(jù)的時(shí)候注意好了。
建議還是按標(biāo)準(zhǔn)行事,把數(shù)據(jù)轉(zhuǎn)化成網(wǎng)絡(luò)字節(jié)序。
PS:
Java與 Windows 平臺(tái)下其他開(kāi)發(fā)語(yǔ)言之間進(jìn)行數(shù)據(jù)交與,也需要遵循該原則;
Java下讀寫(xiě) Windows 平臺(tái)下其他開(kāi)發(fā)語(yǔ)言保存的數(shù)據(jù),或是 Windows 平臺(tái)下其他語(yǔ)言讀寫(xiě)Java 保存的數(shù)據(jù),也需要注意字節(jié)序問(wèn)題。
byte,string區(qū)別:本來(lái)以為傳輸?shù)臅r(shí)候就是string類型字符串,原來(lái)根本不是一回事。網(wǎng)絡(luò)上傳輸數(shù)據(jù)都是字節(jié)碼,就是我們常說(shuō)的 ascII碼的,對(duì)應(yīng)到類型上就是byte類型的。而string只是java中一種對(duì)象而已。而且java中編碼一般是unicode編碼的,要進(jìn)行和byte類型的轉(zhuǎn)化。int和long類型的也要進(jìn)行int2byte轉(zhuǎn)化。
接收到數(shù)據(jù)之后,則要進(jìn)行byte2int轉(zhuǎn)化。且值得注意的是int轉(zhuǎn)成byte并不是直接字符串的轉(zhuǎn)化,比如123轉(zhuǎn)成字符串就是123,但是轉(zhuǎn)成byte就不是了。它要根據(jù)整數(shù)123所占的字節(jié),得到每個(gè)字節(jié)的值,再轉(zhuǎn)成byte數(shù)組。常用的轉(zhuǎn)化函數(shù)有:
//int2byte
public static byte[] intToByte(int n) {
byte[] b = new byte[4];
b[0] = (byte) (n >> 24);
b[1] = (byte) (n >> 16);
b[2] = (byte) (n >> 8);
b[3] = (byte) (n);
return b;
}
public static void int2byte(int n, byte buf[], int offset) {
buf[offset] = (byte) (n >> 24);
buf[offset 1] = (byte) (n >> 16);
buf[offset 2] = (byte) (n >> 8);
buf[offset 3] = (byte) n;
}
// 字節(jié)類型轉(zhuǎn)成int類型
public static int byte2int(byte b[]) {
return b[3] & 0xff | (b[2] & 0xff) << 8 | (b[1] & 0xff) << 16
| (b[0] & 0xff) << 24;
}
//short2byte
public static byte[] short2byte(int n) {
byte b[] = new byte[2];
b[0] = (byte) (n >> 8);
b[1] = (byte) n;
return b;
}
// long到byte的轉(zhuǎn)換
public static byte[] long2byte(long n) {
byte b[] = new byte[8];
b[0] = (byte) (int) (n >> 56);
b[1] = (byte) (int) (n >> 48);
b[2] = (byte) (int) (n >> 40);
b[3] = (byte) (int) (n >> 32);
b[4] = (byte) (int) (n >> 24);
b[5] = (byte) (int) (n >> 16);
b[6] = (byte) (int) (n >> 8);
b[7] = (byte) (int) n;
return b;
}
等等,注意:這里只是進(jìn)行普通的字節(jié)碼和數(shù)值之間的類型轉(zhuǎn)換,并不進(jìn)行高低位轉(zhuǎn)化。原因前面已經(jīng)說(shuō)過(guò)了,java和網(wǎng)絡(luò)字符是一樣的,都是高位前,低位后,所以不用進(jìn)行轉(zhuǎn)化。
中文傳輸問(wèn)題:
網(wǎng)絡(luò)中傳輸中文極易出現(xiàn)亂碼,那怎么辦比較好呢,對(duì)了,就是對(duì)中文進(jìn)行編碼,常用的是Base64編碼。再對(duì)編碼后數(shù)據(jù)進(jìn)行傳輸,接收到后也要先進(jìn)行base64解碼即可。
==================================================
http://www.jscto.net/html/26616.html
struct A
{
int id;
string msg;
};
=============================
雖然,網(wǎng)絡(luò)編程里面的數(shù)據(jù)傳送推薦用序列化,但我不用,還是選擇結(jié)構(gòu)體(返璞歸真),有以下幾點(diǎn)理由:
1.跨平臺(tái)問(wèn)題:
序列化確實(shí)可以很好的跨語(yǔ)言平臺(tái),可大多數(shù)網(wǎng)絡(luò)游戲不需要跨語(yǔ)言平臺(tái)
2.別以為有了序列化就不需要結(jié)構(gòu)體
表面上序列化代碼量小,按順序讀和寫(xiě)char int, short LPCSTR ... 就好,邏輯對(duì)象寫(xiě)不寫(xiě)都無(wú)所謂,那就是大錯(cuò)而特錯(cuò)了
待序列化的對(duì)象/發(fā)送前的結(jié)構(gòu)還是不可省略的。序列化的過(guò)程就是 object->(按一定順序拆分)write->bytes->(按拆分順序組裝)read->object的過(guò)程
其實(shí)object還是不能省略,很多人寫(xiě)網(wǎng)絡(luò)程序不注重邏輯對(duì)象結(jié)構(gòu),收到的一堆bytes按一定順序讀和寫(xiě)就完事了,這樣雖然靈活,但缺乏結(jié)構(gòu),容易造成混亂,調(diào)試起來(lái)是災(zāi)難【ulu的設(shè)備代碼就是這樣的,其實(shí)這就是最基礎(chǔ)原始的序列化】
所以結(jié)構(gòu)體(或類)還是省略不了的,所以:別以為有了序列化,就不需要結(jié)構(gòu)體了。
3.結(jié)構(gòu)體存在內(nèi)存對(duì)齊和CPU不兼容的問(wèn)題,可以避免
的確結(jié)構(gòu)體是有內(nèi)存對(duì)齊的問(wèn)題,存在兼容性問(wèn)題,我可以選擇pack(1)把內(nèi)存對(duì)齊給關(guān)閉掉,避免兼容性問(wèn)題,既然選擇了iocp就不打算跨平臺(tái)了,可以避免結(jié)構(gòu)體平臺(tái)兼容的問(wèn)題
4.結(jié)構(gòu)體調(diào)試起來(lái)方便很多,減少內(nèi)存拷貝,效率高
不用序列化write和read的過(guò)程就不需要過(guò)多考慮(少寫(xiě)太多代碼了),read write 就好像現(xiàn)代社會(huì)每個(gè)人每天都要穿衣服和脫衣服一樣,原始社會(huì)需要嗎?其實(shí)人類進(jìn)化到原始裸奔狀態(tài)才是最爽快的:)
但還是要說(shuō)句公道話:有人說(shuō)序列化編碼解碼read write 需要耗費(fèi)資源, 誠(chéng)然這個(gè)過(guò)程基本等于賦值和內(nèi)存拷貝,那點(diǎn)效率損失主要還在內(nèi)存拷貝上,這點(diǎn)效率損失很小,不能作為序列化的缺點(diǎn),當(dāng)然如果涉及到數(shù)據(jù)加密那將是另外一個(gè)話題
5.結(jié)構(gòu)體貌似呆板,發(fā)送數(shù)據(jù)限制多,發(fā)送變長(zhǎng)數(shù)據(jù)就不方便,數(shù)據(jù)組織起來(lái)也不靈活
我想這是很多人拋棄結(jié)構(gòu)體,選擇用序列化方式發(fā)送和接受數(shù)據(jù)的一個(gè)很重要的原因
但:其實(shí)對(duì)于變長(zhǎng)結(jié)構(gòu)(子結(jié)構(gòu)也是變長(zhǎng))的問(wèn)題,用結(jié)構(gòu)體來(lái)實(shí)現(xiàn)的確很麻煩,但并不代表不能實(shí)現(xiàn)
我已經(jīng)實(shí)現(xiàn)了,而且讀和寫(xiě)變長(zhǎng)子結(jié)構(gòu)體嵌套任意多層都不成問(wèn)題,可以存儲(chǔ)復(fù)雜變長(zhǎng)的數(shù)據(jù)結(jié)構(gòu),
數(shù)據(jù)就如同能自動(dòng)序列化一樣方便,這個(gè)應(yīng)該是技術(shù)難點(diǎn),但細(xì)心去做是可以實(shí)現(xiàn)的
6.關(guān)于結(jié)構(gòu)體指針
游戲里面要發(fā)送的數(shù)據(jù)內(nèi)存事先分配好的,不存在指針,深度復(fù)制更不用考慮,所以內(nèi)存拷貝不會(huì)出錯(cuò)
如果用到指針即使用序列化來(lái)實(shí)現(xiàn)也會(huì)面臨同樣的問(wèn)題也占不了多少便宜,由于C++這們語(yǔ)言的特點(diǎn),
不象java那樣有個(gè)標(biāo)準(zhǔn)實(shí)現(xiàn),對(duì)于序列化本身沒(méi)有一個(gè)統(tǒng)一的標(biāo)準(zhǔn),所以可想而知,有人說(shuō):boost有它的序列化的實(shí)現(xiàn)
其實(shí)那個(gè)實(shí)現(xiàn)不見(jiàn)得就合適你自己,如果真要做序列化,編碼和解碼得仿照那個(gè)過(guò)程自己寫(xiě)才最為牢靠,
哪些指針對(duì)應(yīng)的內(nèi)存需要序列化那些不需要序列化,是個(gè)邏輯結(jié)構(gòu),需要自己說(shuō)了算才好(好像扯遠(yuǎn)了點(diǎn))
說(shuō)回游戲數(shù)據(jù),既然不用需要他用到指針,結(jié)構(gòu)體用來(lái)發(fā)送數(shù)據(jù)也沒(méi)問(wèn)題的
7 平臺(tái)擴(kuò)充問(wèn)題
退一萬(wàn)步的說(shuō):換了語(yǔ)言就基本上換了客戶端,客戶端的數(shù)據(jù)組織形式都要重寫(xiě)
實(shí)在不行還可以考慮用xml json 編碼等等一些跨平臺(tái)的解決方案,現(xiàn)在所寫(xiě)的結(jié)構(gòu)體是可以用來(lái)做數(shù)據(jù)接收的,只是發(fā)送的不再是結(jié)構(gòu)體而已
8.綜上所述
如果需要跨語(yǔ)言平臺(tái),不用序列化(二進(jìn)制流或xml, json文本等等)根本無(wú)法實(shí)現(xiàn)
序列化的優(yōu)點(diǎn)還是非常多的.如果主要是跨平臺(tái)和語(yǔ)言自定義讀寫(xiě)規(guī)則,根據(jù)需要讀寫(xiě)對(duì)象的某一部分?jǐn)?shù)據(jù),
空間浪費(fèi)少,不存在內(nèi)存對(duì)齊問(wèn)題等諸多優(yōu)點(diǎn),缺點(diǎn)就是拐彎抹角,代碼量大,調(diào)試不方便
最好的方法還是模擬rpc的解決方案,利用idl解析器自動(dòng)生成序列化,只是這需要用lex/yacc方面的咚咚,
tao,ice等都有原代碼可借鑒。
聯(lián)系客服