[英]How to Create a Data Packet with Unsigned ints in JAVA
I'm working on a project where I'm writing a java client communicating with a service using TCP over an SSL connection. 我正在一个项目中,我正在编写一个Java客户端,该客户端通过SSL连接使用TCP与服务进行通信。 The service writes all messages to the client fields in network byte order (big endian) and encode text in UTF-8.
该服务以网络字节顺序(大字节序)将所有消息写入客户端字段,并以UTF-8编码文本。 The client does the same.
客户端执行相同的操作。
The format of the message that the client must create to send to the service looks like this 客户端必须创建以发送给服务的消息格式如下所示
| 0 | 1 | 2 | 3 |
|------------------------------------------------|
| Header Version | Message Type |
|------------------------------------------------|
| Message Length |
|------------------------------------------------|
| Initial Timestamp |
|------------------------------------------------|
| Future use | Request Flags |
|------------------------------------------------|
How do I create this data structure in Java with unsigned ints?? 如何在Java中使用无符号整数创建此数据结构?
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer ;
public class NullMessage {
private final int BASE_HEADER_SIZE = 7;
int _headerVersion;
int _messageType;
long _messageLength;
byte[] _options;
public NullMessage( byte[] format ) {
if (format.length == BASE_HEADER_SIZE) {
_headerVersion = (int)( ( ( format[0] << 8 ) & 652080 ) | ( format[1 ] & 255 ));
_messageType = (int)( ( ( format[2] << 8 ) & 652080 ) | ( format[3] & 255 ));
_messageLength = (long)(((format[4]<<24)&4278190080l)
|((format[5]<<16)&16711680l)
|((format[6]<<8)&65280)
|(format[7]&255));
}
else {
throw new RuntimeException("Error in creating NullMessage format");
}
}
public int length() {
return BASE_HEADER_SIZE + (this._options == null ? 0 : this._options.length );
}
public NullMessage( int headerVersion, int messageType, long messageLength) {
this._headerVersion = headerVersion;
this._messageType = messageType;
this._messageLength = messageLength;
}
public byte[] toByteArray() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
out.write( getHeader() );
if ( _options != null ){
out.write( this._options );
}
} catch (IOException e) {
System.out.println( e.toString() ) ;
}
return out.toByteArray() ;
}
// Generate the NullMessage in a byte array format
public byte[] getHeader() {
byte[] nullMessage = new byte[BASE_HEADER_SIZE];
nullMessage[0] = (byte)((_headerVersion>>8)&255);
nullMessage[1] = (byte)((_headerVersion&255));
nullMessage[2] = (byte)((_messageType>>8)&255);
nullMessage[3] = (byte)((_messageType&255));
nullMessage[3] = (byte)((_messageLength>>24)&255);
nullMessage[4] = (byte)((_messageLength>>16)&255);
nullMessage[5] = (byte)((_messageLength>>8)&255);
nullMessage[6] = (byte)((_messageLength&255));
return nullMessage;
}
public byte[] getOption() {
return _options;
}
public int getHeaderVersion() {
return this._headerVersion;
}
public void setHeaderVersion(int version) {
this._headerVersion = version;
}
public int getMessageType() {
return this._messageType;
}
public void setMessageType(int messageType ) {
this._messageType = messageType;
}
public long getMessageLength() {
return this._messageLength;
}
public void setMessageLength( long messageLength ) {
this._messageLength = messageLength;
}
public void print() {
byte[] head = getHeader();
for (int j=0; j<head.length; j++) {
System.out.format("%08X ", head[j]);
}
System.out.println();
}
public static void main(String args[]){
NullMessage test = new NullMessage(1, 0, 0);
test.print();
byte[] array = test.toByteArray();
for (int i=0;i<array.length;i++) {
System.out.println("myByte["+i+"]:"+ Integer.toBinaryString(array[i] & 255 | 256).substring(1));
}
}
} }
使用DataInputStream
和DataOutputStream
的方法。
Unsigned data types do not exist in Java. Java中不存在未签名的数据类型。 What I did as a substitute for unsigned was use the next higher up data type.
我所做的替代unsigned的工作是使用下一个更高的数据类型。
public class MessageFormat
{
// int covers the range of an unsigned short (16 bits)
public int HeaderVersion;
public int MessageType;
// long covers the range of an unsigned int (32 bits)
public long MessageLength
public long InitialTimestamp;
public int Reserved;
public int RequestFlag;
}
Since you're talking about DataPackets from sockets, I used the ByteBuffer class to convert byte arrays to my fields and vice versa. 由于您是在谈论套接字的DataPackets,因此我使用ByteBuffer类将字节数组转换为我的字段,反之亦然。
Here's a sample of ByteBuffer: 这是ByteBuffer的示例:
byte[] arr = { 0x00, 0x01 };
ByteBuffer wrapped = ByteBuffer.wrap(arr); // big-endian by default
short num = wrapped.getShort(); // 1
ByteBuffer dbuf = ByteBuffer.allocate(2);
dbuf.putShort(num);
byte[] bytes = dbuf.array(); // { 0, 1 }
Now the cumbersome part... The way we evaluated bytes was from 0 to 255, Java would evaluate a byte from -127 to +127. 现在麻烦的部分...我们评估字节的方式是从0到255,Java将评估-127到+127的字节。 So what I had to do in this situation was AND the bytes against 0xFF (255) so that I could evaluate them as 0 to 255 instead of -127 to +127.
因此,在这种情况下,我要做的是将字节与0xFF(255)进行“与”运算,以便可以将它们评估为0到255而不是-127到+127。
Here's an example of taking a short
(2 bytes) and promoting it to an int
so that we can have it represent an unsigned short range of 0 - 65535 这是一个采用
short
(2个字节)并将其提升为int
的示例,以便我们可以使其表示无符号的短范围0-65535
byte[] bytes = { (byte)0xFF, (byte)0xFF };
System.out.println(Arrays.toString(bytes));
ByteBuffer bb = ByteBuffer.wrap(bytes);
// As a signed short
System.out.println(bb.getShort());
// Reset back to the beginning
bb.position(0);
// As an "unsigned short", but really an int
System.out.println(bb.getShort() & 0xFFFF);
Output: 输出:
[-1, -1]
-1
65535
When a field is supposed to be "unsigned" for an outgoing message, then: 如果应该为外发消息“无符号”字段,则:
For the message size, for example, if your particular client is designed never to send very long messages (that is, not longer than Integer.MAX_VALUE
), for example, if it always sends well-known length-limited message payloads, then you can actually use a plain int
when you calculate it. 对于消息大小,例如,如果您的特定客户端被设计为从不发送非常长的消息(即,不超过
Integer.MAX_VALUE
),例如,如果它始终发送众所周知的长度受限制的消息有效负载,则您在计算它时实际上可以使用一个普通的int
。
If it is conceivable that it will send very long messages, then you should use a long
to calculate the size (eg long calculatedSize
). 如果可以想象它会发送很长的消息,那么您应该使用
long
来计算大小(例如, long calculatedSize
)。 Then check that it does not exceed the capacity of a signed integer, eg: 然后检查它是否不超过有符号整数的容量,例如:
if ( calculatedSize & 0xffffffffL != calculatedSize ) {
throw new InvalidMessageException();
}
( InvalidMessageException
is not an existing type. Just an Exception type that you will create for this purpose). (
InvalidMessageException
不是一个现有的类型。您将为此创建一个Exception类型)。
And then you can use (int)calculatedSize
for your actual data. 然后,您可以将
(int)calculatedSize
用于实际数据。 This simply drops the most significant 4 bytes, which you have made sure are zero anyway, leaving you with the least significant 4 bites. 这只会丢弃最高有效的4个字节,无论如何,您必须确保已将其设为零,从而使最低有效的4位字节保留下来。
The fact that Java interprets this integer as signed is not relevant, because the only use you'll make of it is to write its bytes to the message. Java将这个整数解释为带符号的事实并不重要,因为您将要使用的唯一用途是将其字节写入消息中。
The same goes for the time stamp field. 时间戳字段也是如此。 Here it's very likely you'll need to go beyond signed int capacity (which will break by the year 2038).
在这里,您很有可能需要超出有符号的int容量(这将在2038年之前打破)。 So work with
long
. 所以要
long
工作。 In fact, use the usual Java idiom of milliseconds since the epoch, and then when you need to create the message header, divide by 1000, and do the same check and the same conversion as I've shown above. 实际上,请使用自该纪元以来的毫秒数的 Java惯用法,然后在需要创建消息头时,除以1000,然后执行与上面所示相同的检查和相同的转换。
General advice: 一般建议:
ByteBuffer
(which allows you to set the particular byte order), and put the various fields in it, some directly from the constants, and the message length and time stamp calculated from the fields, using putInt()
for your 4-byte integers and putWord()
for your 2-byte integers. ByteBuffer
(允许您设置特定的字节顺序),并将各种字段(其中一些直接来自常量)以及消息长度和时间戳记放入其中。从田地计算的,使用putInt()
为您的4字节整数和putWord()
为您的2字节整数。 Socket()
, you should consider working with channels, as they work very well with ByteBuffer
objects. Socket()
,则应考虑使用通道,因为它们与ByteBuffer
对象一起使用时效果很好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.