如何通过套接字发送与c++兼容的Java结构体

How to send by socket C++ compatible struct from Java?

本文关键字:Java 结构体 c++ 何通过 套接字      更新时间:2023-10-16

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

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

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

如何从Java Socket发送这样的数据?我试图用putDouble()和putLong()填充ByteBuffer,也假设putInt()为32位,但我无法产生有效值。

我也随机生成和发送数据,并且结构级字节分配看起来很好(我只能随机化一个值,例如X),但是我不能生成精确的值(可能是不同的双精度表示?),而只能通过发送Math.random() * double . max_value;

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

请记住,我不能更改服务器(接收)端

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

我看到这里至少有两个可能的问题:

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

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

在几个小时内,我将为"字节破解"提供精确的示例数据(发送前的精确Java值和c++中接收的double值)

关于服务器不良设计应答

我知道这样的实现和不可更改的服务器是坏软件,但在这个问题中的环境是注入一些数据到小的老应用程序在"业余水平"

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

假设已经有一个写好的、不可改变的宿主程序,通过套接字

接收这样的c++结构体

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

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

强烈建议您抓住机会修复整个兆拉,并为交易定义一个适当的电线格式。

一旦你做到了这一点,你就可以很容易地用DataOutputStream的工具来模拟它,或者如果问题更糟,涉及到端序,NIO加ByteBuffer.

我明白了。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();
}

和putDouble()/putInt()使用bytebuffer

您需要与服务器在平台上验证用于双端(通常是IEEE标准,但不一定是)和长端(大端对小端)的编码。您还需要确定所涉及的填充。

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

还有,找到那个决定在你无法更改的服务器上使用隐式二进制协议的人,并对他们使用橡皮软管密码分析:

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

如果不知道服务器平台的所有细节,这是不容易做到的。

Java在所有平台上都有浮点数和整型数的标准表示,但c++没有定义这些东西。