6

假设已经编写了一个不可更改的主机程序,它通过套接字接收这样的 C++ 结构:

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

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

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

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

我可以只在一侧(客户端生成数据)使用 Google 协议缓冲区来解决这个问题吗?

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

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

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

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

这些东西在 C++ 中的 Java 中是什么样子的?

几个小时后,我将为“字节黑客”提供准确的示例数据(发送前的准确 Java 值和 C++ 中接收到的 double 值)

关于服务器不良设计回答

我知道这种实现且不可更改的服务器是糟糕的软件,但这个问题的环境是在“hobbistic 级别”向小型旧应用程序注入一些数据

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
4

3 回答 3

3

假设已经有编写的、不可更改的主机程序通过套接字接收这样的 C++ 结构

那么你已经有一个问题,某个不知名的人好心地留给你作为遗产。您现在必须生成 Java 代码以符合特定 C++ 编译器对特定 的有线格式的概念struct,这取决于:

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

强烈建议您借此机会修复整个 megillah 并为交易定义适当的电汇格式。

一旦你完成了,你可以很容易地使用 的工具来模拟它DataOutputStream,或者如果问题更严重并且涉及字节序,NIO plusByteBuffer.

于 2013-05-13T08:36:58.623 回答
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() 一起使用。

于 2013-05-13T11:15:04.087 回答
1

您需要在服务器的平台上验证用于双精度(通常是 IEEE 标准,但并非必须如此)和长(大端与小端)的编码。您还需要确定所涉及的任何填充。

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

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

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

在不了解服务器平台的所有细节的情况下,没有简单的方法可以做到这一点。

Java 在所有平台上都有一个标准的浮点数和整数表示,但 c++ 没有定义这些东西。它是特定于 c++ 的平台/架构。

于 2013-05-13T08:21:15.977 回答