简体   繁体   English

如何在JAVA中创建具有无符号整数的数据包

[英]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         |
 |------------------------------------------------|
  1. Header Version - Unsigned 16 bits - Value is always 1 标头版本-无符号16位-值始终为1
  2. Message Type - Unsigned 16 bits 消息类型-无符号16位
  3. Message Length - Unsigned 32 bits 消息长度-无符号32位
  4. Initial Timestamp - Unsigned 32 bits 初始时间戳-无符号32位
  5. Reserved - Unsigned 16 bits -Always set to 0 保留-无符号16位-始终设置为0
  6. Request Flag - bits[16] 请求标志-位[16]

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

    }
}

} }

使用DataInputStreamDataOutputStream的方法。

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: 如果应该为外发消息“无符号”字段,则:

  • The side that needs to interpret it as unsigned is the server. 需要将其解释为未签名的一面是服务器。
  • On the client side, you simply need to make sure you are doing your calculations in such a way that will not overflow the number. 在客户端,您只需要确保以不会溢出数字的方式进行计算即可。

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: 一般建议:

  • You'll probably do best to design your message as a class that has all the various constants (header version, message types, future use, various flags), and instance fields for the time stamp and the actual message payload. 您可能会最好地将消息设计为具有所有各种常数(标头版本,消息类型,将来的使用,各种标志)以及时间戳和实际消息有效负载的实例字段的类。 Don't worry about the sign of your constants. 不用担心常量的符号。 They are not subject to calculations and therefore their sign is irrelevant. 它们不受计算,因此它们的符号无关紧要。
  • This class should have a method for creating a message header, which will use a 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字节整数。
  • In case you were planning to work with a plain 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM