Wednesday, April 16, 2008

Как java и .net пишут числа в поток.

Учавствуя в разработке клиентского приложения на Java и серверного на .Net, встретил интересную несовместимость платформ в записи/чтения чисел в/из бинарный поток . Все началось с того, что в первых четырех байтах(int) каждой порции данных посылаемых из приложения, работающего на устройствах типа CLDC, передавался размер этой порции. На серверной же стороне этот размер не рассчитывался правильным образом. Оказалось, что Java записывает(читает) в формате big-endian, а .Net в little-endian.

Для прояснения картины приведу следующие два примера:

1.C#

            MemoryStream stream= new MemoryStream(new byte[4]);
BinaryWriter writer = new BinaryWriter(stream);
int num = 218893066;
Console.WriteLine("Двоич. форма: " + Convert.ToString(num, 2));
Console.WriteLine("Шестн. форма: " + num.ToString("X"));
writer.Write(num);
byte[] arr=stream.ToArray();
Console.WriteLine("Первый байт: "+arr[0]);
Console.WriteLine("Второй байт: " + arr[1]);
Console.WriteLine("Третий байт: " + arr[2]);
Console.WriteLine("Четвертый байт: " + arr[3]);
Console.ReadLine();
2.Java
ByteArrayOutputStream baos = new ByteArrayOutputStream(4);
DataOutputStream dos = new DataOutputStream(baos);
int num= 218893066;
System.out.println("Двоич. форма: "+Integer.toBinaryString(num));
System.out.println("Шестн. форма: "+Integer.toHexString(num));
dos.writeInt(num);
byte[] arr=baos.toByteArray();
System.out.println(""Первый байт: "+arr[0]);
System.out.println("Второй байт: " + arr[1]);
System.out.println("Третий байт: " + arr[2]);
System.out.println("Четвертый байт: " + (arr[3]));
System.in.read();

Результат 1:


Двоич. форма:1101000011000000101100001010
Шестн. форма: D0C0B0A
Первый байт: 10
Второй байт: 11
Третий байт: 12
Четвертый байт: 13


Результат 2:

Двоич. форма:1101000011000000101100001010
Шестн. форма: d0c0b0a
Первый байт: 13
Второй байт: 12
Третий байт: 11
Четвертый байт: 10


Число 2188930066 специально выбрано для примера, что бы не осложнять картину тем, что в Java byte - знаковый байт(signed byte), а в C# byte - безнаковый байт(unsigned byte). В C# есть знаковый байт(sbyte), а вот в Java такого(таких) типа нет(но разговор сейчас не об этом).

Поскольку моей целью не являлось продолжение священной войны между Лилипутией и Блефуску , то проблема была решена очень просто. Т.к. в моем случае клиентская платформа(j2me) имеет очень много ограничений, было решено изменить формат порядка байт на сервере.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SystemBinaryReader = System.IO.BinaryReader;

namespace Mmg.Ssp.MobileGateway.Entropy.Protocol.Hybrid
{
class BinaryReader : SystemBinaryReader
{
public BinaryReader(Stream input, Encoding encoding) : base(input, encoding)
{
}

public BinaryReader(Stream input) : base(input)
{
}

public override int ReadInt32()
{
byte b1 = ReadByte();
byte b2 = ReadByte();
byte b3 = ReadByte();
byte b4 = ReadByte();
return (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0);
}
}
}
using System.IO;
using System.Text;
using SystemBinaryWriter = System.IO.BinaryWriter;

namespace Mmg.Ssp.MobileGateway.Entropy.Protocol.Hybrid
{
public class BinaryWriter : SystemBinaryWriter
{
public BinaryWriter(Stream output, Encoding encoding) : base(output, encoding)
{
}

public BinaryWriter(Stream output) : base(output)
{
}

public BinaryWriter()
{
}

public override void Write(int value)
{
OutStream.WriteByte((byte)(value >> 24 & 0xff));
OutStream.WriteByte((byte)(value >> 16 & 0xff));
OutStream.WriteByte((byte)(value >> 8 & 0xff));
OutStream.WriteByte((byte)(value & 0xff));
}
}
}

Или воспользоваться готовыми решениями. Так же можно воспользоваться классами из J#(vjslib.dll) , которые используют формат big-endian (как и java), но здесь возникает проблема в том, что J# является "копией" java и использует знаковые байты, в отличие от C#(да и вообще, по моим ощущениям, Microsoft откажется от поддержки J#, так например, среды для него уже нет в Microsoft Visual Studio 2008), поэтому это наверное не лучшее решение.

1 comment:

  1. Hi,
    nice article.
    Reminds me about old good times and VBScript long type that is not as "long" as it sounds.

    ReplyDelete