我正在开发一个与在Windows服务器上运行的 C程序进行通信的应用程序,该程序是使用Visual Studio开发的(如果此信息可以提供任何帮助)。
服务器通过套接字通信向我发送一个整数,在发送服务器之前做了以下事情: -
现在接收端我有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 };
}
}
答案 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,则会产生错误的结果。
原因如下:
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。