http://www.cnblogs.com/my_life/articles/4943353.html http://www./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ā)送端和接收端,通常不能保證是兩邊是相同的編程語言,就算都是使用C語言,CPU字節(jié)序,或者CPU位數(shù)不一樣,直接將結(jié)構(gòu)體的數(shù)據(jù)整理成流發(fā)送過去,數(shù)據(jù)排序或者長(zhǎng)度會(huì)跟你想象的不一樣。解釋起來比較費(fèi)篇幅。 這里說下通常的解決辦法:
http://www.cnblogs.com/kaijia9/p/3394953.html UDP傳輸模式是數(shù)據(jù)報(bào),TCP傳輸模式為字節(jié)流,字節(jié)流與數(shù)據(jù)報(bào)區(qū)別在于有邊界與無邊界。例如:TCP客戶端發(fā)送了三個(gè)數(shù)據(jù)包,開的緩存足夠大服務(wù)端一次可接收三個(gè)數(shù)據(jù)包的數(shù)據(jù),這就是無邊界。UDP客戶端發(fā)送了三個(gè)數(shù)據(jù)包,就算開的緩存足夠大服務(wù)端一次也只能接收一個(gè)數(shù)據(jù)包,這就是有邊界。 還有就是協(xié)議會(huì)維護(hù)源地址和目的地址直到協(xié)議要求斷開連接,這就決定了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是無邊界的字節(jié)流傳輸,所以需要將結(jié)構(gòu)體轉(zhuǎn)換為字符串后在發(fā)送,最后三行用了兩種方法發(fā)送屬于結(jié)構(gòu)體類型的數(shù)據(jù),通過TCP傳輸。最后在接收方需要轉(zhuǎn)換為結(jié)構(gòu)體。 紅色: 數(shù)組屬于字符串,該方法是將要發(fā)送結(jié)構(gòu)體所占字節(jié)大小考到數(shù)組中, 再通過數(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 { 原始的序列化:將結(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。
所以,我們通過socket的send發(fā)送結(jié)構(gòu)體或者對(duì)象的時(shí)候要注意了,需要序列化,當(dāng)然,大家可以說,這樣一個(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é)屏蔽了。所以,一般通過send發(fā)送結(jié)構(gòu)體不能直接把它轉(zhuǎn)換成char*的字節(jié)序列發(fā)送,在發(fā)送之前,要先做序列化。
以下給出用Qt的QDataStream做序列化例子: http://www./Code/Cpp/Qt/SerializationwithQDataStream.htm http://comments./gmane.comp.lib.qt.general/38559
注意:char型的數(shù)據(jù)是不用序列化的,因?yàn)橹皇菃蝹€(gè)字節(jié),不是多字節(jié)占用 references: http:///questions/5894622/sending-any-structure-via-qtcpsocket http:///questions/2473300/overloading-the-qdatastream-and-operators-for-a-user-defined-type http:///questions/1577161/passing-a-structure-through-sockets-in-c http:///questions/17817280/send-struct-over-socket-in-c
======================= http://hcq0618.blog.163.com/blog/static/1780903512013101831120514/
主要技術(shù)問題: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)體來模擬報(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用來接收消息(貌似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等】都沒做大小端轉(zhuǎn)換,對(duì)其他的short, int, long等都需要大小端轉(zhuǎn)換
short 或者 long的數(shù)據(jù)在進(jìn)行通信的時(shí)候最好養(yǎng)成: 1、發(fā)送的時(shí)候使用:htons(l) http:///unp/2015/10/15/unp-socket/ 在《Linux高性能服務(wù)器編程》中這里理解更好些:
知道為什么有模式的存在,下面需要了解應(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é),無所謂大端和小端。而對(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的值寫入到內(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é)序 網(wǎng)絡(luò)字節(jié)序 同樣 在網(wǎng)絡(luò)程序開發(fā)時(shí) 或是跨平臺(tái)開發(fā)時(shí) 也應(yīng)該注意保證只用一種字節(jié)序 不然兩方的解釋不一樣就會(huì)產(chǎn)生bug. Java 程序與 C++ 之間的 SOCKET 通訊
byte,string區(qū)別:本來以為傳輸?shù)臅r(shí)候就是string類型字符串,原來根本不是一回事。網(wǎng)絡(luò)上傳輸數(shù)據(jù)都是字節(jié)碼,就是我們常說的 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)說過了,java和網(wǎng)絡(luò)字符是一樣的,都是高位前,低位后,所以不用進(jìn)行轉(zhuǎn)化。
中文傳輸問題: 網(wǎng)絡(luò)中傳輸中文極易出現(xiàn)亂碼,那怎么辦比較好呢,對(duì)了,就是對(duì)中文進(jìn)行編碼,常用的是Base64編碼。再對(duì)編碼后數(shù)據(jù)進(jìn)行傳輸,接收到后也要先進(jìn)行base64解碼即可。
================================================== http://www./html/26616.html struct A 不建議發(fā)結(jié)構(gòu)體,因?yàn)?span style="color: #ff0000;">即便雙方采用同樣的語言和編譯器,也可能因?yàn)樘畛渥止?jié)的不同而導(dǎo)致結(jié)構(gòu)體計(jì)算的大小不同。建議嚴(yán)格規(guī)定各個(gè)字段的大小和意義,依次發(fā)送。寫到byte[]發(fā)送。
=============================
雖然,網(wǎng)絡(luò)編程里面的數(shù)據(jù)傳送推薦用序列化,但我不用,還是選擇結(jié)構(gòu)體(返璞歸真),有以下幾點(diǎn)理由: 2.別以為有了序列化就不需要結(jié)構(gòu)體 3.結(jié)構(gòu)體存在內(nèi)存對(duì)齊和CPU不兼容的問題,可以避免 4.結(jié)構(gòu)體調(diào)試起來方便很多,減少內(nèi)存拷貝,效率高 5.結(jié)構(gòu)體貌似呆板,發(fā)送數(shù)據(jù)限制多,發(fā)送變長(zhǎng)數(shù)據(jù)就不方便,數(shù)據(jù)組織起來也不靈活 6.關(guān)于結(jié)構(gòu)體指針 7 平臺(tái)擴(kuò)充問題 8.綜上所述 最好的方法還是模擬rpc的解決方案,利用idl解析器自動(dòng)生成序列化,只是這需要用lex/yacc方面的咚咚,
|
|