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
网络数据传输,可以直接发送字符串,但不能直接发送一个结构体。 网络上传输数据,因为发送端和接收端,通常不能保证是两边是相同的编程语言,就算都是使用C语言,CPU字节序,或者CPU位数不一样,直接将结构体的数据整理成流发送过去,数据排序或者长度会跟你想象的不一样。解释起来比较费篇幅。 这里说下通常的解决办法:
http://www.cnblogs.com/kaijia9/p/3394953.html UDP传输模式是数据报,TCP传输模式为字节流,字节流与数据报区别在于有边界与无边界。例如:TCP客户端发送了三个数据包,开的缓存足够大服务端一次可接收三个数据包的数据,这就是无边界。UDP客户端发送了三个数据包,就算开的缓存足够大服务端一次也只能接收一个数据包,这就是有边界。 还有就是协议会维护源地址和目的地址直到协议要求断开连接,这就决定了TCP不能进行广播和多播。
直接发送结构体的方式【是不对的】: · 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是无边界的字节流传输,所以需要将结构体转换为字符串后在发送,最后三行用了两种方法发送属于结构体类型的数据,通过TCP传输。最后在接收方需要转换为结构体。 红色: 数组属于字符串,该方法是将要发送结构体所占字节大小考到数组中, 再通过数组发送。 蓝色: 将该结构体地址转化为char* 类型的地址,目的是使该指针加1移动时 是按一个字节移动,而不是加1按该结构体大小移动,然后发送该结构 体所占字节大小。
struct AP_INFO { 原始的序列化:将结构体的成员一个一个的复制到内存再发到服务端
=============================================== http://www.cnblogs.com/foohack/p/4718320.html 大家都知道,在进行网络传输的时候,因为分布在网络上的每台机器可能大小端的不同,需要进行字节序列转换,比如用win32 API的socket里面就有类似与htonl等与此类似的函数,它就是把主机端的字节序列转换成网络传输的字节序列。当然也有与之相反的函数ntohl,是把网络字节序,转换为主机字节序。
比如 int data = 0x32461256在小端机器上按照“高高低低”的原则,内存上是这样表示,0x56,0x12,0x46,0x32。进行htonl转换后,在内存中的布局就会变成0x32,0x46,0x12,0x56。
所以,我们通过socket的send发送结构体或者对象的时候要注意了,需要序列化,当然,大家可以说,这样一个结构体那么多字段都要手动用htonl之类的函数序列化,那么太麻烦,其实网络上有专门的序列化库,比如google的protobuff,boost也有相应模块,Qt的QDataStream内部就实现了序列化,序列化实际上就是把大小端,还有结构体字节对齐等细节屏蔽了。所以,一般通过send发送结构体不能直接把它转换成char*的字节序列发送,在发送之前,要先做序列化。
以下给出用Qt的QDataStream做序列化例子: http://www./Code/Cpp/Qt/SerializationwithQDataStream.htm http://comments./gmane.comp.lib.qt.general/38559
注意:char型的数据是不用序列化的,因为只是单个字节,不是多字节占用 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/
主要技术问题:windows,linux等系统采用LITTLE_ENDIAN字节序,而java自身采用BIG_ENGIAN字节序,BIG_ENGIAN是指低地址存放最高有效字节(MSB),而LITTLE_ENDIAN则是低地址存放最低有效字节。Java程序写的客户程序端同c++的服务端程序交互时结构体的某些数据类型需要转换字节序。本文解决方法,java客户端程序发送数据时做相应的转换字节序,等收到数据时再做一次字节序的转换。 现在的网络程序多数采用可靠交付的TCP协议,其采用字节流的传输方式,c++程序中用结构体来模拟报头以此界定每次发送的报文。所以网络中整个字节流的格式:报头+数据负载+报头+数据负载……
c++与java进行socket通信时注意事项 因为java发送的都是网络字节序(big-endium),而c++是主机字节序(little-endium),所以当消息中有整型,浮点型(应尽量避免使用)的时候需要用htonl,htons,ntohl,ntohs等函数转换一下,字符串由于是单字节排序的不需要转换,但应注意c++字符串是以'/0'作为结束符的,如果找不到'/0'可能会出现一些乱码,所以接收的时候可以分配一个length+1的buffer用来接收消息(貌似java会自动处理). 网络只有字节的概念,所以,你必须把你要传送的东西全部转换成字节后,再发送出去,在c中,以字节方式存在的数据,是不需要进行转换的,比如char *什么的
可参考qiyi的ChatMsg.h中的序列化函数:对单字节字段【char, char *, char [], 可当作char*的string等】都没做大小端转换,对其他的short, int, long等都需要大小端转换
short 或者 long的数据在进行通信的时候最好养成: 1、发送的时候使用:htons(l) http:///unp/2015/10/15/unp-socket/ 在《Linux高性能服务器编程》中这里理解更好些:
知道为什么有模式的存在,下面需要了解应用场景: 1、不同端模式的处理器进行数据传递时必须要考虑端模式的不同 2、在网络上传输数据时,由于数据传输的两端对应不同的硬件平台,采用的存储字节顺序可能不一致。所以在TCP/IP协议规定了在网络上必须采用网络字节顺序,也就是大端模式, 对于char型数据只占一个字节,无所谓大端和小端。而对于非char类型数据,必须在数据发送到网络上之前将其转换成大端模式。接收网络数据时按符合接受主机的环境接收。
struct { char one; //字符 unsigned short two; //short类型 unsigned int three; //int类型 char * four; //字符串 }BinaryProtocolStruct; //one为char类型不需要进行网络主机传输模式转换,把one的值写入到内存块中 //two为unsigned short 类型,所以要进行网络主机的传输字节顺序的转换 htons //three 为int类型 所以要进行网络主机的传输字节顺序的转换 htonl //four为字符串不需要进行存储转换
主机字节序与网络字节序 网络字节序 同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug. Java 程序与 C++ 之间的 SOCKET 通讯
byte,string区别:本来以为传输的时候就是string类型字符串,原来根本不是一回事。网络上传输数据都是字节码,就是我们常说的 ascII码的,对应到类型上就是byte类型的。而string只是java中一种对象而已。而且java中编码一般是unicode编码的,要进行和byte类型的转化。int和long类型的也要进行int2byte转化。 接收到数据之后,则要进行byte2int转化。且值得注意的是int转成byte并不是直接字符串的转化,比如123转成字符串就是123,但是转成byte就不是了。它要根据整数123所占的字节,得到每个字节的值,再转成byte数组。常用的转化函数有: //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; }
// 字节类型转成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的转换 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; } 等等,注意:这里只是进行普通的字节码和数值之间的类型转换,并不进行高低位转化。原因前面已经说过了,java和网络字符是一样的,都是高位前,低位后,所以不用进行转化。
中文传输问题: 网络中传输中文极易出现乱码,那怎么办比较好呢,对了,就是对中文进行编码,常用的是Base64编码。再对编码后数据进行传输,接收到后也要先进行base64解码即可。
================================================== http://www./html/26616.html struct A 不建议发结构体,因为即便双方采用同样的语言和编译器,也可能因为填充字节的不同而导致结构体计算的大小不同。建议严格规定各个字段的大小和意义,依次发送。写到byte[]发送。
=============================
虽然,网络编程里面的数据传送推荐用序列化,但我不用,还是选择结构体(返璞归真),有以下几点理由: 2.别以为有了序列化就不需要结构体 3.结构体存在内存对齐和CPU不兼容的问题,可以避免 4.结构体调试起来方便很多,减少内存拷贝,效率高 5.结构体貌似呆板,发送数据限制多,发送变长数据就不方便,数据组织起来也不灵活 6.关于结构体指针 7 平台扩充问题 8.综上所述 最好的方法还是模拟rpc的解决方案,利用idl解析器自动生成序列化,只是这需要用lex/yacc方面的咚咚,
|
|
来自: liang1234_ > 《序列化》