2019独角兽企业重金招聘Python工程师标准>>>

c++与java进行socket通信时注意事项

因为java发送的都是网络字节序(big-endium),而c++是主机字节序(little-endium),所以当消息中有整型,浮点型(应尽量避免使用)的时候需要用htonl,htons,ntohl,ntohs等函数转换一下,字符串由于是单字节排序的不需要转换,但应注意c++字符串是以'/0'作为结束符的,如果找不到'/0'可能会出现一些乱码,所以接收的时候可以分配一个length+1的buffer用来接收消息.

举例:c++ server, java client,假设开发的是c++ server,那么:

java client--------->c++ server: c++ server需要调用ntohs,ntohl
c++ server--------->java client: c++ server需要调用htons,htonl

至于浮点型可以使用以下的函数转换:

float tcp_htonf(float f)
{ unsigned char *p, p0, p1; if(htons(1) ==1) return f; p =(unsigned char *)&f;p0 =p[0]; p1 =p[1];p[0] =p[3];p[3] =p0;p[1] =p[2];p[2] =p1; return f;
}float tcp_ntohf(float f)
{unsigned char *p, p0, p1; if(ntohs(1) ==1) return f; p =(unsigned char *)&f; p0 =p[0]; p1 =p[1]; p[0] =p[3]; p[3] =p0; p[1] =p[2]; p[2] =p1; return f;
}
double tcp_htond(double d)
{  unsigned char *p, p0, p1, p2, p3;    if(htons(1) ==1) return d;  p =(unsigned char *)&d;  p0 =p[0];  p1 =p[1];  p2 =p[2];  p3 =p[3]; p[0] =p[7];  p[7] =p0;  p[1] =p[6];  p[6] =p1;??  p[2] =p[5];??  p[5] =p2; p[3] =p[4];  p[4] =p3;  return d;
}
double tcp_ntohd(double d)
{ unsigned char *p, p0, p1, p2, p3; if(ntohs(1) ==1) return d;    p =(unsigned char *)&d; p0 =p[0];  p1 =p[1];  p2 =p[2];  p3 =p[3];  p[0] =p[7];  p[7] =p0;  p[1] =p[6];  p[6] =p1;  p[2] =p[5];p[5] =p2;p[3] =p[4]; p[4] =p3;return d;
}

java代码发送结构体
最近给个朋友做个网站的客户端,使用C/S模式,Client为VC6开发,Server为Java,通过Socket通信。由于Client这边为C++,所以,在接受Java发过来的数据包时,需要知道发来的包的长度,所以,就要引入变长包的机制。
方法是:首先Server发送一个包头,如下:
// packet head
typedef struct tagPacketHead{
long PacketID;
long PacketLen;
}PacketHead;
包头后面跟上包体,其中包体的长度,就是上面结构体中的PacketLen,Clinet首先接受包头,因为包头是两边约定好的,所以可以直接Receive一个定长的消息,也就是这个包头的长度的消息,从包头中取得包体的长度后,就可以再次Receive一个包体长度的消息了。那么Java中如何发送一个结构体呢?下面是解决方法:

package org.charry.org;
import java.net.*;
/**
*
* 字节转换,参考网络文章  
*/
class Packet {
private byte[] buf = null;
/**
* 将int转为低字节在前,高字节在后的byte数组
*/
private static byte[] toLH(int n) {
byte[] b = new byte[4];
b[0] = (byte) (n & 0xff);
b[1] = (byte) (n >> 8 & 0xff);
b[2] = (byte) (n >> 16 & 0xff);
b[3] = (byte) (n >> 24 & 0xff);
return b;
}
/**
* 将float转为低字节在前,高字节在后的byte数组
*/
private static byte[] toLH(float f) {
return toLH(Float.floatToRawIntBits(f));
}
/**
* 构造并转换
*/
public Packet(int packetID, int packetLen, String packetBody) {
byte[] temp = null;
buf = new byte[packetBody.getBytes().length + 8];
temp = toLH(packetID);
System.arraycopy(temp, 0, buf, 0, temp.length);
temp = toLH(packetLen);
System.arraycopy(temp, 0, buf, 4, temp.length);
System.arraycopy(packetBody.getBytes(), 0, buf, 8,packetBody.length());
}
/**
* 返回要发送的数组
*/
public byte[] getBuf() {
return buf;
}
/**
* 发送测试
*/
public static void main(String[] args) {
try {
String tmp = “test string!”;
Socket sock = new Socket(”127.0.0.1″, 8888);
sock.getOutputStream().write(
new Packet(123, tmp.length(), tmp).getBuf());
sock.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
从Client端发到Server的数据就无须特殊处理了,Java的流可以很好的处理这些。

主机字节序与网络字节序
主机字节序
不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序 
最常见的有两种
1. Little endian:将低序字节存储在起始地址
2. Big endian:将高序字节存储在起始地址

LE little-endian 
最符合人的思维的字节序 
地址低位存储值的低位 
地址高位存储值的高位 
怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说 
低位值小,就应该放在内存地址小的地方,也即内存地址低位 
反之,高位值就应该放在内存地址大的地方,也即内存地址高位

BE big-endian 
最直观的字节序 
地址低位存储值的高位 
地址高位存储值的低位 
为什么说直观,不要考虑对应关系 
只需要把内存地址从左到右按照由低到高的顺序写出 
把值按照通常的高位到低位的顺序写出 
两者对照,一个字节一个字节的填充进去

例子:在内存中双字0x01020304(DWORD)的存储方式 
内存地址 
Addr   4000 4001 4002 4003 
LE   04   03   02   01 
BE   01   02   03   04

例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
      big-endian   little-endian
0x0000   0x12       0xcd
0x0001   0x34       0xab
0x0002   0xab       0x34
0x0003   0xcd       0x12
x86系列CPU都是little-endian的字节序.

网络字节序
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
为了进行转换 bsd socket提供了转换的函数 有下面四个
htons 把unsigned short类型从主机序转换到网络序
htonl 把unsigned long类型从主机序转换到网络序
ntohs 把unsigned short类型从网络序转换到主机序
ntohl 把unsigned long类型从网络序转换到主机序
在使用little endian的系统中 这些函数会把字节序进行转换 
在使用big endian类型的系统中 这些函数会定义成空宏
同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug.

注:
1、网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)
2、不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。
处理器     操作系统     字节排序
Alpha      全部      Little endian
HP-PA        NT       Little endian
HP-PA        UNIX     Big endian
Intelx86     全部     Little endian  
<!--v:3.2--> 
java与C++之间进行SOCKET通讯要点简要解析
分类: Android/Java2011-03-01 08:58 2000人阅读 评论(2) 收藏 举报
目录(?)[+]
 =======================================================================================
java与 C++ 之间进行 SOCKET 通讯要点简要解析

0、篇外语
  此乃本人学习过程中自娱自乐之作,为了遗忘后有个地方再温习。如入您法眼,转载请尊重原作者,请说明出处。

1、 big-endian 与 little-endian
   Endian定义: 在计算机系统体系结构中用来描述在多字节数中各个字节的存储顺序。
big-endian也称高位在前、大端在前。是 计算机体系结构中一种描述多字节存储顺序的术语,在这种机制中最重要字节(MSB )存放在最低端的地址 上。采用这种机制的处理器有Mortolora  PowerPC 微处理器系列和绝大多数的 RISC 处理器。
big-endian 最直观的字节序:
内存地址从左到右与值由低到高的顺序相对应。
little-endian也称低位在前、小端在前。 计算机体系结构中一种描述多字节存储顺序的术语,在这种机 制中最不重要字节(LSB )存放在最低端的地 址上。采用这种机制的处理器有 Intel x86 系列微处理器和一些网络通信设备。该术语除了描述多字节存储顺序外还常常用来描述一个字节中各个比特的排放次序 ,这里仅讨论多字节存储循序 。
little-endian是最符合人的思维的字节序,低与低,高与高一一对应:
地址低位存储值的低位 
地址高位存储值的高位
  
     下面举一个例子具体说明 big-endian 与 little-endian:
      int  nValue = 0x01020304;
      上面的整型nValue 有 4 个字节,其中 01 为最高位的字节, 04 为最低位的字节。那么在内存(或文件)中,该值的存储循序为:
      内存(或文件)地址:0x12000001  0x12000002  0x12000003  0x12000004  
      Big-endian         :      01         02           03         04
      Little-endian        :      04         03           02         01
    
      如果用一个byte 数组来保存的话,也就是如下:
      Big-endian模式下:  byte  byValue[] = {0x01, 0x02, 0x03, 0x04};
      Little-endian模式下: byte  byValue[] = {0x04, 0x03, 0x02, 0x01};
Big-endian 或是 little-endian的判断:
bool   IsLittleEndian ()
{
       int     i       = 1;  
       char  * p       = ( char *)& i ;  
   i f    (   * p    =   1   )            
          return   true ;    // 小端 
   else
  return   false ;   // 大端
}

2、网络字节序与主机字节序
在各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。如果不达成一致的规则, 通信双方 将无法进行正确的编/ 译码从而导致通信失败。
通常所说的网络字节序(Network Byte Order )就是遵循 big-endian 规则。实际通信过程中,通信双方需要把数据按照 big-endian 编码再通过网络传输。
通常所说的主机字节序(Host Byte Order ),与 CPU 的字节序一致。 x86 系列主机的字节序都是 little-endian 桂册。所有 little-endian 规则主机直接通过网络通讯的时候,需要进行字节序转化。
    为了进行转换 bsd socket 提供了转换的函数 有下面四个
 htons 把 unsigned short 类型从主机 字节 序转换到 网络字节序
 htonl 把 unsigned long 类型从主机 字节 序转换到 网络字节序
 ntohs 把 unsigned short 类型从 网络字节序 转换到主机 字节 序
     ntohl 把 unsigned long 类型从网络 字节 序转换到主机 字节 序
    在使用little endian 的系统中这些函数会把字节序进行转换
    在使用big endian 类型的系统中这些函数会定义成空宏

3、 java 字节序
由于Java 运行需要自己的虚拟机来支持,所以 Java 程序所支持的字节序与 Java 虚拟机一致。Java 虚拟机遵循的是 big-endian 规则。所以可以把 Java 字节序看作是遵循 big-endian 规则的主机字节序。

4、 Java 程序与 C++ 之间的 SOCKET 通讯
4.1 字节序问题
一直以来都在进行着C++ 上面的网络开发,发现在 C++ 上面进行通讯的时候,基本上都没有考虑到网络字节序的问题,特别是网络应用中的用户数据。大家都知道网络通讯传输的都是字节流的数据,于是都是定义一个 char 类型的缓冲区,然后不管 int , WORD, DWORD 还是其他自定义类型的结构对象也好,都直接 memcpy() 拷贝到缓冲区,直接发送出去了,根本都没有考虑所传输数据的网络字节序问题。如果非要说一点关注了网络字节序问题的话,那就是有一个地方,大家都回去用到的,也就是网络通讯端口,把端口号赋值给 sockaddr_in .sin_port之时大家都会使用了htons() ,也就是把端口号从主机字节序转化为网络字节序。
因为这些程序的服务器端也好,客户端也好,都是在x86 系列系统下运行,并且都是 C++ 编译出来的,所以不会因为字节序而出现问题。
现在所做项目,涉及到Java 与 C++ 之间的 SOCKET 通讯,这样的情况下,就需要大家都按规则来办事了, C++ 方面传输的多字节类型数据还请从主机字节序转化成网络字节序再进行传输。
当然,数据是由程序员来组合的,也是由程序员来解析的,所以如果不按标准行事也是可以的。但是就需要在解析数据的时候注意好了。
建议还是按标准行事,把数据转化成网络字节序。
     PS:
     Java与 Windows 平台下其他开发语言之间进行数据交与,也需要遵循该原则;
     Java下读写 Windows 平台下其他开发语言保存的数据,或是 Windows 平台下其他语言读写Java 保存的数据,也需要注意字节序问题。

4.2 字节对齐问题
#include   <iostream>
using   namespace   std ;
typedef   struct   tag_S1
{
     char   s_szValue [8];
     char   s_cValue ;
}  S1 ;
typedef   struct   tag_S2
{
     int    s_nValue1 ;
     char   s_szValue [8];
     char   s_cValue ;
     int    s_nValue2 ;
}  S2 ;
typedef   struct   tag_S3
{
     int    s_nValue ;
     char   s_cValue ;
}  S3 ;
#pragma   pack ( push , 1)
typedef   struct   tag_S4
{
     int    s_nValue ;
     char   s_cValue ;
}  S4 ;
#pragma   pack ( pop )
int   main ( int   argc ,  char *  argv [])
{   
     cout  <<  "sizeof(S1):"  <<  sizeof ( S1 ) <<  endl ;
     cout  <<  "sizeof(S2):"  <<  sizeof ( S2 ) <<  endl ;
     cout  <<  "sizeof(S3):"  <<  sizeof ( S3 ) <<  endl ;
     cout  <<  "sizeof(S4):"  <<  sizeof ( S4 ) <<  endl ;
     system ( "pause" );
     return  0;
}
上面的程序在 WinXP sp3 + VS2008Sp1下运行结果如下:
sizeof(S1):9
sizeof(S2):20
sizeof(S3):8
sizeof(S4):5
请按任意键继续. . .
Win32位平台下的微软 C 编译器 (cl.exe for 80x86) 的对齐策略:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
2) 结构体每个成员相对于结构体首地址的偏移量( offset )都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节( internal adding );
备注: 为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节( trailing padding )。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。   
    Windows 32位系统下, VC 中,默认的字节对齐方式是 4 字节对齐。
sizeof(S1) :因为结构中数据类型都是 char ,最宽基本类型大小是 1 ,所以结构大小为 9, S1 没有进行填充;
sizeof(S2)、 sizeof(S3) : S2 , S3 就被填充了一定的字节;
sizeof(S4):因为设置了对齐方式为 1 字节对齐,所以不会被填充。
在Java 与 C++ 进行 SOCKET 通讯的 C++ 端程序,建议涉及网络通讯的结构使用 1 字节对齐方式,不然 Java 端会增加数据处理的复杂度。

4.3 Java与 C++ 之间基本数据类型的差别
需要注意以下几个数据类型的区别(32 位系统下 ) :
      C++                       Java
  char---------1byte          Byte----------1byte
                              Char----------2byte2
  long---------4bytes         long----------8bytes
注意:
Java中的 Char 是一个字符,而不是一个字节,与 VC 的 WORD 长度一致;
Java中的 Byte 是一个字节,与 C++ 中的 char 含义一致,而 VC 中的 BYTE 是无符号的char ;
Java中的 long 长度为 8 ,而 VC 中的 long 长度为 4 ( C++ 中 short , long 的长度跟编译器的实现相关)。

转载于:https://my.oschina.net/ypimgt/blog/106439

Java与C++Socket通讯注意相关推荐

  1. Java与C++Socket通讯注意事项

    c++与java进行socket通信时注意事项 原文链接: http://my.oschina.net/ypimgt/blog/106439 因为java发送的都是网络字节序(big-endium), ...

  2. java安卓模拟器和电脑通信_Android 模拟器(JAVA)与C++ socket 通讯 分享

    C++ 作为Client端 view plaincopy to clipboardprint? // Client.cpp : Defines the entry point for the cons ...

  3. socket通讯工具类

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.i ...

  4. java与 C++ 之间进行 SOCKET 通讯要点简要解析

    Endian定义: 在计算机系统体系结构中用来描述在多字节数中各个字节的存储顺序. big-endian也称高位在前.大端在前.是 计算机体系结构中一种描述多字节存储顺序的术语,在这种机制中最重要字节 ...

  5. as3 java 交互_求大佬用 Java 实现这段 AS3 的 socket 通讯功能

    最近在分析一个直播网站,初步分析后发现是在 swf 中用 socket 通讯返回的 flv 地址. 其中 Actionscript socket 通讯的关键代码如下: this._socket = n ...

  6. Socket网络通讯开发总结之:Java 与 C进行Socket通讯

    先交待一下业务应用背景: 服务端:移动交费系统:基于C语言的Unix系统 客户端:增值服务系统:基于Java的软件系统 通迅协议:采用TCP/IP协议,使用TCP以异步方式接入 数据传输:基于Sock ...

  7. socket java 服务器端_Java 简单的Socket通讯的服务器端实现

    最近学安卓开发,看到书上这个Socket通讯的实例,按照书上打的时候客户端一直连接不上,可能是网络问题或者虚拟机连接问题,所以就用控制台实现的客户端,这里是服务器端的.话不多说,直接贴代码 impor ...

  8. java socket通讯_Java socket通讯实现过程及问题解决

    这篇文章主要介绍了Java socket通讯实现过程及问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本来是打算验证java socket是 ...

  9. java socket 卡住_Java socket通讯实现过程及问题解决

    这篇文章主要介绍了java socket通讯实现过程及问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本来是打算验证java socket是 ...

最新文章

  1. Google App Engine给我们带来了什么?
  2. IO系统性能之二:缓存和RAID如何提高IO
  3. php开发者大会报名,2017 PHP 全球开发者大会
  4. 小白适用的C语言数据类型转换及转换规则
  5. 如何在MySQL中创建新用户并授予权限
  6. ReactiveX流式编程—从xstream讲起
  7. JavaScript求最小公倍数
  8. 中信证券:维持贝壳“买入”的投资评级
  9. Horizon View 7 发布Win10桌面三:链接克隆桌面池配置
  10. 论文笔记-LSHTC: A Benchmark for Large-Scale Text Classification-2015
  11. [转载] Python3基础:08_02_面向对象编程(OOP)——类和对象
  12. 连接mongoDB根据ObjectID写入json数据(初步)
  13. 文档大小超出上传限制怎么办_有道翻译和翻译狗,哪个更适合翻译文档?
  14. html如何制作艺术字体,设计华丽金沙艺术字体图片的PS教程
  15. jar包反编译工具(java-decompiler)
  16. tensorflow实现数据增强(随机裁剪、翻转、对比度设置、亮度设置)
  17. zcmu Problem E: 喜闻乐见的a+b
  18. UBUNTU14.04 的rabbitvcs问题
  19. 纯CSS实现Table固定表头和首列
  20. URL中的%2C是什么?

热门文章

  1. 松翰松翰c语言编程指导,松翰C程序检单例程代码下载
  2. php 云片网对接,php调用云片网接口发送短信的实现方法
  3. c语言界面怎么加图形,「分享」C语言如何编写图形界面
  4. 服务器默认字符集 网页乱码,关于apache默认字符集乱码的问题
  5. java 集合 自动排序的_java中的自动排序集合 ---- 20160809
  6. 买了一个软件测试就业班课程,不知道值不值
  7. 计算机基础操作与应用实训教程,计算机应用基础实训教程の第 3 章 Word 2003 基本操作.pdf...
  8. android 折叠与展开,android – 如何根据滚动方向折叠/展开视图?
  9. was修改堆内存_C语言内存泄露很严重,如何应对?
  10. java gpio_Java控制树莓派GPIO口-Pi4J