如何在Java中获取memcpy?

时间:2012-06-21 13:06:55

标签: java android

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

服务器通过套接字通信向我发送一个整数,在发送服务器之前做了以下事情: -

  1. 声明一个int
  2. 分配一些值
  3. 使用 memcpy
  4. 将2个字节复制到char *(比如缓冲区)
  5. 向该缓冲区添加更多数据
  6. 发送缓冲区
  7. 现在接收端我有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 的最佳方法是什么?

    我访问了this帖子,但这没有多大帮助,

    修改 我实施了以下四种适合我的方法,

    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 };
        }
    }
    

2 个答案:

答案 0 :(得分: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阻止模式”。

答案 1 :(得分:1)

此代码:

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

肯定是不正确。它可能适用于您尝试过的某些值,但肯定可以使用其他值。

如果recvBuf[1]不是零,则会产生错误的结果。

如果recvBuf[0]大于127,则会产生错误的结果。

原因如下:

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

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

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

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

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

将结果分配给int而不是short将保证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。