4

我正在开发一个与 Windows 服务器上运行的C 程序通信的应用程序,该程序是使用 Visual Studio 开发的(如果此信息有任何帮助)。

服务器通过套接字通信向我发送一个整数,在发送服务器之前执行以下操作:-

  1. 声明一个整数
  2. 赋予它一些价值
  3. 使用memcpy将 2 个字节复制到 char * (比如缓冲区)
  4. 向该缓冲区添加更多数据
  5. 发送该缓冲区

现在在接收端我有java实现,所以不能直接使用memcpy,我用过

short mId = java.nio.ByteBuffer.wrap(recvBuf, 0, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();

可以,很好,但是这部分代码每隔几毫秒就会调用一次,所以我正在尝试优化它..我也使用过

short mId =(short)(recvBuf[0] + recvBuf[1]*128);

这也可以正常工作,但我怀疑如果将来数量增加,它是否会起作用。在 java中重复memcpy的最佳方法是什么?

我已经访问过这个线程,但这并没有太大帮助,

编辑 我实现了以下四种对我有用的方法,

public class CommonMethods {
    /*
     * Returns the byte[] representation of an int in Little Endian format
     * 
     * @param value that should be converted to byte[]
     */
    public static byte[] toByteArray(int value) {
        return new byte[] { (byte) value, (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
    }

    /*
     * Returns the int in LittleEndian value of the passed byte[]
     * 
     * @param bytes is the input byte[]
     * 
     * @param offset is the offset to start
     */
    public static int getInt(byte[] bytes, int offset, int length) {
        int retValue = (bytes[offset] & 0xFF);
        byte bVal;
        for (int i = 1; i < length; i++) {
            bVal = bytes[offset + i];
            retValue |= ((bVal & 0xFF) << (8 + (8 * (i - 1))));
        }
        return retValue;
    }

    /*
     * Returns the int in BigEndian from the passed byte[]
     * 
     * @param bytes is the byte[]
     */
    public static int getIntBigEndian(byte[] bytes, int offset, int length) {
        int retValue = (bytes[offset + length - 1] & 0xFF);
        for (int i = 1; i < length; i++) {
            retValue |= ((bytes[offset + length - 1 - i] & 0xFF) << (8 + (8 * (i - 1))));
        }
        return retValue;
    }

    /*
     * Returns the byte[] representation of an int in Big Endian format
     * 
     * @param value that should be converted to byte[]
     */
    public static byte[] toByteArrayBigEndian(int value) {
        return new byte[] { (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value };
    }
}
4

2 回答 2

3

可以ByteBuffer直接收到。这节省了一些系统调用/内存副本。

ByteBuffer buf = ByteBuffer.allocate(1024); // just allocate enough, can be reused later;
buf.reset();  // in case this is reused, you need reset()

SocketChannel channel = socket.getChannel();
channel.read(buf);

short mId = buf.order(ByteOrder.LITTLE_ENDIAN).getShort();

byte[] otherBytes = new byte[....];
buf.get(otherBytes);

当然,您需要检查实际读取的长度。为了清楚起见,我省略了它们。如果您想了解更多信息,可以谷歌“java nio 阻塞模式”。

于 2012-06-21T13:51:41.900 回答
1

这段代码:

short mId =(short)(recvBuf[0] + recvBuf[1]*128);

绝对是不正确的。它可能适用于您尝试过的某些值,但绝对不适用于其他值。

如果recvBuf[1]不是零,这将给出不正确的结果。

如果recvBuf[0]大于 127,这将给出不正确的结果。

原因如下:

  • byte在 Java 中已签名。如果字节中的值介于 128 和 255 之间,它将被解释为负数(这可能不是您想要的)
  • 乘以recvBuf[1]128 是不正确的。您需要乘以 256 才能正确放大该值。另见上述关于负数的观点

假设您正在以 LITTLE-ENDIAN 字节顺序从 C 写入字节,您可以这样做:

int mId = ((recvBuf[0] & 0xFF) | ((recvBuf[1] & 0xFF) << 8));

用于仅屏蔽值的& 0xFF低 8 位,以防设置了高位(因为 Java 将在将字节转换为 int 时对其进行符号扩展)。

<< 8功能等同于* 256,但它是一个位移函数,而不是算术函数,它更适合这个用例。

将结果分配给 aint而不是 ashort将保证 32768 和 65535 之间的值被正确解释为正数而不是负数。

这将适用于 0 到 65535 之间的所有值。一旦您的数字变得更大,无论如何发送时您将无法将其放入 2 个字节中。如果您想为未来做计划并认为您可能需要超过 2 个字节,那么只需复制 C 整数的所有 4 个字节并读取 Java 中的值,如下所示:

long mId = (long)(((recvBuf[0] & 0xFF) | ((recvBuf[1] & 0xFF) << 8) |
              ((recvBuf[2] & 0xFF) << 16) | ((recvBuf[3] & 0xFF) << 24)) & 0xFFFFFFFF);

另请注意,Java 默认字节顺序是 BIG-ENDIAN,而不是 LITTLE-ENDIAN。

于 2012-06-21T13:57:42.397 回答