如何从Java发送socket C ++兼容结构?

时间:2013-05-13 08:04:26

标签: java c++ sockets struct double

假设已有写入的,不可更改的宿主程序,它通过socket接收这样的C ++结构:

#pragma pack(push, 2)
struct Data {
  double x;
  double y;
  double z;
  long frameNumber;
};
#pragma pack(pop) 

platform:在Visual Studio 2008中编译的C ++ / 32位Windows应用程序

如何从Java Socket发送此类数据?我已经尝试用putDouble()填充ByteBuffer,putLong(),putInt()假设长达32位,但我无法生成有效值。

我也随机生成和发送数据,结构级字节赋值看起来很好(我只能随机化一个值,例如X),但我不能产生精确的值(可能是不同的双重表示?),但只是随机的发送Math.random()* Double.MAX_VALUE;

我是否可以仅在一方(产生数据的客户端)上使用Google Protocol Buffers来解决此问题?

请记住,我无法更改服务器(接收方)

我知道我可以将数据发送到C ++并使用JNI,但我正在寻找更简单的解决方案。

我在这里看到至少2个可能的问题:

  • 字节级的双重表示(如01234567和另一侧的76543210)
  • 一个字节中的位表示(如00000001和另一侧的1000000)

这些东西在Java中用C ++看起来是什么?

在几个小时内,我将提供“字节黑客”的精确样本数据(发送前的确切Java值和C ++中收到的double的值)

关于服务器错误设计回答

我知道这种已实现且不可更改的服务器是不好的软件,但这个问题的环境是将一些数据注入“hobbistic-level”的小型旧应用程序

Java表示

也许这会对某些位级C ++专家有所帮助:

Long.toBinaryString( Double.doubleToRawLongBits( (double) 0 ) );
// 000000000000000000000000000000000000000000000000000000000000000

Long.toBinaryString( Double.doubleToRawLongBits( 1 ) );
// 011111111110000000000000000000000000000000000000000000000000000

Long.toBinaryString( Double.doubleToRawLongBits( 1.1 ) );
// 011111111110001100110011001100110011001100110011001100110011010

Long.toBinaryString( Double.doubleToRawLongBits( 1024 ) );
// 100000010010000000000000000000000000000000000000000000000000000

Long.toBinaryString( Double.doubleToRawLongBits( 1024.1024 ) );
// 100000010010000000000000110100011011011100010111010110001110001

Long.toBinaryString( Double.doubleToRawLongBits( Double.MAX_VALUE ) );
// 111111111101111111111111111111111111111111111111111111111111111

3 个答案:

答案 0 :(得分:3)

  

假设已经编写了不可更改的宿主程序,它通过套接字

接收这样的C ++结构

然后你已经有一个主要的问题,一些不知名的人已经把你留作遗产了。您现在必须生成Java代码以符合特定C ++编译器对特定struct的有线格式的概念,这取决于:

  • 硬件
  • 编译器供应商
  • 编译器版本
  • 编译选项
  • 周围的#pragmas
  • ...

强烈建议您利用这个机会修复整个megillah并为事务定义正确的 wire 格式。

完成后,您可以使用DataOutputStream的设施轻松地模拟它,或者如果问题更严重并且涉及到字节序,则NIO加ByteBuffer.

答案 1 :(得分:3)

我明白了。 Int值被发送正常(可能,在主机程序中无法检查这100%确定),问题在于双精度端。你需要在发送之前转换双打:

public static double changeEndian( double x ) {
      ByteBuffer cv = ByteBuffer.allocate( 8 ).putDouble( x );
      cv.order( ByteOrder.LITTLE_ENDIAN );
      cv.rewind();
      return cv.getDouble();
}

使用ByteBufer和putDouble()/ putInt()。

答案 2 :(得分:1)

您需要在服务器平台上验证用于double(通常是IEEE标准,但不一定是)以及long(big vs little endian)的编码。您还需要识别所涉及的任何填充。

然后,如果它们不是java本机编码,则需要手动构造适合发送的二进制值。这可能是非常重要的,特别是对于浮点值,如果你在一个相当混乱的服务器平台上。

此外,追踪那些决定对您无法更改的服务器使用隐式二进制协议的人,并使用橡胶软管密码分析:

http://en.wikipedia.org/wiki/Rubber-hose_cryptanalysis

在不了解服务器平台的所有细节的情况下,没有简单的方法。

Java在所有平台上都有浮点数和整数的标准表示,但c ++没有定义这些东西。它是针对c ++的平台/体系结构。