简体   繁体   中英

Unsigned short to byte array

I have to send an short as an unsigned short to a TCPServer.

The Problem is that Java doesn't support usigned shorts: What I have tried:

byte[] data = new byte[3];
short port = 5025;

data[0] = 1;
data[1] = (byte)(port & 0xff);
data[2] = (byte)((port >> 8) & 0xff);

And this is the way how i convert the data to an unsigned short in C++

// Bytes to Short (uint16)
unsigned short port = (data[1] << 8) | data[2];

So how can I solve this problem in Java? (I doesn't want to change andything in the C++ Code)

Edit:// My new Java code:

byte[] data = new byte[3];
short port = 1151; // short or int doesn't matter in this case

ByteBuffer buffer = ByteBuffer.allocate(5);
buffer.put((byte) 1);
buffer.putShort(port);

out.write(buffer.array());

The C++ Code: (the same)

unsigned short port = (data[1] << 8) | data[2];

I get the right port, if the port is between 0-1151 and 16384-32767, but why doesn't it work with the rest?

It does not matter. Just put it in a short . That a short is signed is of no concern; a short is still 16 bits.

What matters is the endianness here. And if you send over a network, it's big endian.

Which is the default for a ByteBuffer , and the default for all numeric primitive types for Java.

So, what do you do? For your particular example, this:

// Just for a short...
final ByteBuffer buf = ByteBuffer.allocate(3);
buf.put((byte) 1);
buf.putShort(myShort);
final byte[] contents = buf.array();
// send the byte[]

Now, if you have more to .put*() in the ByteBuffer , allocate whatever space is necessary etc.


HOWEVER. You say you don't want to change anything to your C++ code... Which is not portable across architectures. If you wish to read/write 16 bit values over the network in C++, use ntohs()/htons() .

(maybe there are better APIs than that; I haven't done network programming at an advanced level in C/C++ for quite some time)

I have a slightly different point of view. The OP is using bit shifting correctly to get into little endian, so the C++ portability will be fine unless he's dealing with a byte of unconventional size. The communication protocol goes against the big endian over network convention, but sometimes supporting legacy systems is like that.

If the port variable has users outside the provided code, use an int and only send the bits you want as you did in your Java sample above. If you are passing that port around, it sucks to keep having to twiddle the damn sign bits and sooner or later you'll screw it up. If no one else needs to play with port, sign isn't going to matter.

byte[] data = new byte[3];
int port = 5025; // short or int doesn't matter in this case

data[0] = 1;
data[1] = (byte)(port & 0xff);
data[2] = (byte)((port >> 8) & 0xff);

When reading back in and getting 65440, it looks like you used a char and your bytes got sign extended by the shift. Here is a bit of test code so you can play around and see what's happening.

#include <cstdio>

int main()
{
    unsigned short val = 32896;
    char hi = (char)((val >> 8) & 0xFF);
    char lo = (char)(val &0xFF);
    printf("Watch what the sign bit can do to the bytes here:\n");
    printf("Value: %d, raw in hex: %04x, Hi byte: %02x, Low byte: %02x\n", val, val, hi, lo);



    printf("This one only works if the low byte doesn't sign extend\n");
    char datas[3] = {0, hi, lo};
    unsigned short port = (datas[1] << 8) | datas[2];
    printf("Reassembled short: %u, In Hex: %04x\n", port, port);

    printf("This one works, but will not for an integer\n");
    port = (datas[1] << 8) | (datas[2] & 0xFF);
    printf("Reassembled short: %u, in Hex: %04x\n", port, port);
    unsigned int bigport = (datas[1] << 8) | (datas[2] & 0xFF);
    printf("Reassembled int: %u, in Hex: %04x\n", bigport, bigport);

    printf("With unsigned characters it just works\n");
    unsigned char datau[3] = {0, hi, lo};
    port = (datau[1] << 8) | datau[2];
    printf("Reassembled short: %u, In Hex: %04x\n", port, port);
    bigport = (datau[1] << 8) | (datau[2] & 0xFF);
    printf("Reassembled int: %u, in Hex: %04x\n", bigport, bigport);
}

output:

Watch what the sign bit can do to the bytes here:
Value: 32896, raw in hex: 8080, Hi byte: ffffff80, Low byte: ffffff80
This one only works if the low byte doesn't sign extend
Reassembled short: 65408, In Hex: ff80
This one works, but will not for an integer
Reassembled short: 32896, in Hex: 8080
Reassembled int: 4294934656, in Hex: ffff8080
This one just works
Reassembled short: 32896, In Hex: 8080
Reassembled int: 32896, in Hex: 8080

So what happened?

(datas[1] << 8) | datas[2]

Both numbers must be scaled up to short and they are signed, so 0x80 becomes 0xFF80. Actually, they become integers, but that's another story.

(0xFF80 << 8) | 0xFF80

Simplifies to

0x8000 | 0xFF80

And that ORs to

0xFF80

AKA 65408, not 32896.

In this case unsigned char is your friend. There may have been problems with the Java, but the C++ is definitely broken.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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