1

Of course I know this have to do with endianess. I know Java is big-endian while C# is little-endian.

I have a working program in Java which sends a string to an external system. The thing is that they require to use a 2 byte header at the beggining of the string that represents its length.

In this 2 byte header, the first one is just to tell if the string exceeds 255 characters, if not, it is always 0. But the second byte (which is the one that i can't get right in C#) will represent the length of the string, if the first byte is 0 (in almost all cases) it represents the whole lenth of the string, let's say 226.

So in java, I have this segment of code that calculates the header and append the original string so we can have the final string to send, and it works just fine:

Java working segment of code:

String iso_str = iso_buf.toString();    // This contains original string without header

int iso_len = iso_str.length();     // We need to know if it exceeds 255

int i, j;
i = (int)(iso_len / 256);           // Always 0. It would be 1 if original string bigger than 255
j = (int)(iso_len % 256);           // Length of the string (most times 226)

Integer ii = new Integer(i);
Integer jj = new Integer(j);

byte[] bmsg_0200 = new byte[iso_len + 2];   // We create an array of bytes making room for the 2 bytes header and the original string
bmsg_0200[0] = ii.byteValue();      // Header byte number one
bmsg_0200[1] = jj.byteValue();      // Header byte number two

System.arraycopy(iso_str.getBytes(), 0, bmsg_0200, 2, iso_str.length()); // Then we just copy the original string in the array after the header         

String mensaje_str = new String(bmsg_0200); // Make the whole byte array into one string to send

In the part that says: bmsg_0200[1] = jj.byteValue(); is where java actually gets a byte value that works (and I think it puts -30 there when jj is 226). And the final system understands this header, therefore reads all the message.

I tried to replicate the code in .NET (C#) and I have the following code:

C# not working segment of code:

int tramaISOLongitud = iso.Length;  // iso contains original string without header, We need to know if it exceeds 255

int i, j;
i = (int)(tramaISOLongitud / 256);  // Always 0. It would be 1 if original string bigger than 255
j = (int)(tramaISOLongitud % 256);  // Length of the string (most times 226)            

byte[] tramaISOBytes = new byte[tramaISOLongitud + 2];  // We create an array of bytes making room for the 2 bytes header and the original string
tramaISOBytes[0] = Convert.ToByte(i);   // Header byte number one
tramaISOBytes[1] = Convert.ToByte(j);   // Header byte number two

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
System.Array.ConstrainedCopy(encoding.GetBytes(iso), 0, tramaISOBytes, 2, tramaISOLongitud); // Then we just copy the original string in the array after the header

System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); 
string tramaISOHeader = enc.GetString(tramaISOBytes); // Make the whole byte array into one string to send

The last code compiles and everything, but gets the wrong byte in tramaISOBytes[1] = Convert.ToByte(j); because is little-endian. So i tried using System.Net.IPAddress.HostToNetworkOrder as follows:

int tramaISOLongitud = iso.Length;  // iso contains original string without header, We need to know if it exceeds 255

int i, j;
i = (int)(tramaISOLongitud / 256);  // Always 0. It would be 1 if original string bigger than 255
j = (int)(tramaISOLongitud % 256);  // Length of the string (most times 226)    

int i2 = System.Net.IPAddress.HostToNetworkOrder(i);
int j2 = System.Net.IPAddress.HostToNetworkOrder(j);    

byte[] tramaISOBytes = new byte[tramaISOLongitud + 2];  // We create an array of bytes making room for the 2 bytes header and the original string
tramaISOBytes[0] = Convert.ToByte(i2);  // Header byte number one
tramaISOBytes[1] = Convert.ToByte(j2);  // Header byte number two

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
System.Array.ConstrainedCopy(encoding.GetBytes(iso), 0, tramaISOBytes, 2, tramaISOLongitud); // Then we just copy the original string in the array after the header

System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); 
string tramaISOHeader = enc.GetString(tramaISOBytes); // Make the whole byte array into one string to send

But then j2 gets a huge number (like -503316480) that cannot be converted into a single byte because is too large and for sure i need it in one byte because the header as I said, is just 2 byte long. Any ideas?

Thanks in advance.


OK I will put this on a much simpler way:

In C#:

int test    = -10471344;
int test2   = -503316480;
int test3 = 57856;
byte b = (byte)test;
byte b2 = (byte)test2;
byte b3 = (byte)test3;

If we run these lines we get: b = 80 b2 = 0 b3 = 0

So, why the number -10471344 casted to a (byte) gives 80; and the other ones just give 0? Actually I'm interested in the variable test2 = -503316480 I would like it to be converted in something else, and i get 0. Why b can be 80 and the other ones cannot be something different than 0?


SOLUTION: As Jim said, my problem didn't have anything to do with endianness (although I thought it was everything related to that), but the key knowledge is that in .NET world converting a String into a byte array is no problem, but getting back from byte array to string is where you have problems because you have to use encoding (ASCIIEncoding or UTF8Enconding) and that's where problems arise because your bytes get mangled.

In java I didn't have this kind of problem.

So this is the block of code that did the job:

static byte[] addHeader2(string iso)
{
    int tramaISOLongitud = iso.Length;

    byte highByte = (byte)(tramaISOLongitud >> 8);
    byte lowByte = (byte)(tramaISOLongitud & 255);

    byte[] tramaISOBytes = new byte[tramaISOLongitud + 2];
    tramaISOBytes[0] = highByte;
    tramaISOBytes[1] = lowByte;

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    System.Array.ConstrainedCopy(encoding.GetBytes(iso), 0, tramaISOBytes, 2, tramaISOLongitud);

    return tramaISOBytes;
}

And I just send that byte array through the socket as it is.

THANKS TO ALL FOR YOUR HELP!

4

2 回答 2

2

您的问题与字节序无关。当你在做算术时,系统是大端还是小印度都没有关系。也就是说,鉴于此:

int len = 260;

byte highByte = (byte)(len >> 8);
byte lowByte = (byte)(len & 255);

Console.WriteLine("highByte = {0:X2}", highByte);
Console.WriteLine("lowByte = {0:X2}", lowByte);

highByte永远是1 ,永远4。lowByte

无需进行 HostToNetWorkOrder 转换。

现在,如果您使用负值,您的除法和 mod 运算符会给您带来意想不到的结果。但是你说它是一个 2 字节长度的值,你存储在一个 int 中,所以负值不应该给你带来问题。

我认为,如果您单步执行原始 C# 代码,您会看到正确的值存储在tramaISOBytes数组的前两个位置。我怀疑当您尝试将生成的字节数组转换为字符串时会出现问题。您正在使用ASCIIEncoding将字节数组转换为字符串,这将破坏任何大于 127 (0x7Fh) 的字节。

所以,而不是你的转换是:

System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
string tramaISOHeader = enc.GetString(tramaISOBytes);

您想使用 8 位编码。也许像 ISO-8859-2 这样的东西。基本上,您想要一些不会尝试进行任何转换的东西。或者,更好的是,如果你把它放在一起进行传输,你甚至不应该转换回字符串。直接传输字节数组即可。这样一来,事情就没有被破坏的机会。

于 2012-04-12T17:01:49.877 回答
0

将 2 个字节复制到一个字节数组中,并尝试将字节转换为 ushort:

bool remoteSystemIsLittleEndian = false;
byte[] stringLengthBytes = new byte[] { 1, 255 };

if (BitConverter.IsLittleEndian != remoteSystemIsLittleEndian)
{
    Array.Reverse(stringLengthBytes);
}

int stringLength = (int)BitConverter.ToUInt16(stringLengthBytes, 0);

这用于将数字转换回 2 字节数组:

bool remoteSystemIsLittleEndian = false;
int stringLength = 511;

byte[] stringLengthBytes = BitConverter.GetBytes((ushort)stringLength);

if (BitConverter.IsLittleEndian != remoteSystemIsLittleEndian)
{
    Array.Reverse(stringLengthBytes);
}
于 2012-04-12T15:49:51.347 回答