[Java] String omzetten naar byte array

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • iChaos
  • Registratie: December 2009
  • Laatst online: 14-10 07:21

iChaos

It's Lupus.

Topicstarter
Ik ben momenteel bezig met een applicatie voor Android. Hierbij moet er via UDP een commando gestuurd worden. Dat commando komt voort uit het OSC protocol, en zal dus omgeleid worden naar een OSC-compatible DAW. Met behulp van de tool KonkreetPerformer kan ik zien wat er gebeurt op die UDP poort, en de commando's komen wel aan, maar blijkbaar zijn ze in het onjuiste formaat, waardoor hij ze weigert.

Het desbetreffende stuk code ziet er als volgt uit:
code:
1
2
3
4
5
 byte[] buf = ("/live/clip/view 1, 1").getBytes();
                    
                    /* Create UDP-packet with
                     * data & destination(url+port) */
                    DatagramPacket packet = new DatagramPacket(buf, buf.length, serverAddr, 9003);


De tool die de UDP berichten logt en het probeert om te zetten naar OSC, geeft hierbij echter als error:

OSC Bad message Name String: DataAfterAlignedString: Unreasonably long string Dropping entire message


Die byte array van buf heb ik laten outputten naar logcat, zodat ik kan zien wat de waarde op dat moment was. Dat gaf als resultaat: [B@40517450

Die waarde leek me iets te kort, omdat er naar mijn weten 2 tekens (als in 0xFF) worden gebruikt per character, en dus leek het me vreemd dat de byte array hier kleiner uitvalt dan de oorspronkelijke string. Ik heb geprobeerd om een andere charset te kiezen bij het getBytes gedeelte, maar UTF-8 of ASCII gaven eveneens deze aparte resultaten (niet exact dezelfde output in de logcat, maar het grootste deel was gelijk).

Er gaat dus volgens mij iets mis bij deze conversie, en wat zoekwerk gaf niet veel resultaten. Heeft iemand misschien een idee waarom deze conversie naar een byte array verkeerd gaat, en wat ik er aan kan doen?

Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

Dit is de toString() van een byte[], niet de toString() van de inhoud van de byte array.

iets als

Java:
1
2
for (byte b : buf)
    System.out.print(String.format("%02X ", b));


zou je meer info moeten geven.

Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

[B@40517450 betekent niet meer dan dat er een byte ( B ) array ( [ ) bekend is bij je systeem met object id 40517450. Of er iets fout gaat met je conversie kun je daar dus niet uit afleiden, tenzij je inderdaad een toString-call of iets vergelijkbaars doet.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:41
Volgens de spec moet een string gecodeerd worden met tussen één en vier terminating zero characters (zodanig dat de lengte een veelvoud van vier is). Die heb je er nu sowieso niet bijzitten, want getBytes() returnt alleen de character data.

Verder moeten alle parameters correct gecodeerd worden; je stuurt dus niet een string "/live/clip/view 1,1" maar de string "/live/clip/view" gevolgd door twee getallen. Die hebben weer hun eigen codering.

De string die je uiteindelijk verstuurd zal dus iets als "/live/clip/view\0\1\0\0\0\1\0\0\0" zijn. Daarbij moet je de protocolspecificatie volgen.

[ Voor 4% gewijzigd door Soultaker op 29-04-2012 02:39 ]


Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
Naast bovenstaand advies moet je eigenlijk nooit getBytes() gebruiken, maar getBytes(Charset charset) of getBytes(String charsetName) . Anders is het afhankelijk van het default platform characterset wat de inhoud van de byte array is.

Acties:
  • 0 Henk 'm!

  • iChaos
  • Registratie: December 2009
  • Laatst online: 14-10 07:21

iChaos

It's Lupus.

Topicstarter
Ik heb op internet gezocht naar voorbeelden van OSC commando's, en toen stuitte ik op het volgende:

byte[] buf = ("/oscillator/4/frequency\0,f\0\0\0\0\0\0").getBytes("ASCII");

Dit pakketje heb ik laten verzenden, en dit komt prima aan zonder fouten, en wordt gewoon gezien als een valide OSC commando. Ik heb nu dus gekeken hoe de commando's moeten zijn wanneer Ableton Live ze interpreteert, en volgens een API zou dat gaan volgens het patroon (in dit geval):

/live/clip/view (int track, int clip)

Op basis hiervan dacht ik dat het commando wat verzonden moest worden, uit zou moeten komen als

byte[] buf = ("/live/clip/view\0,ii\0\0").getBytes("ASCII");

Dit commando wordt echter niet als valid gezien, en ik kan ook niet afleiden waar de argumenten voor de 2 integers worden ingesteld. Weet iemand misschien waarom deze code geweigerd wordt, en hoe ik die integers als argumenten meegeef?

Acties:
  • 0 Henk 'm!

  • Feanathiel
  • Registratie: Juni 2007
  • Niet online

Feanathiel

Cup<Coffee>

iChaos schreef op zondag 29 april 2012 @ 13:58:
[..]

byte[] buf = ("/live/clip/view\0,ii\0\0").getBytes("ASCII");

Dit commando wordt echter niet als valid gezien, en ik kan ook niet afleiden waar de argumenten voor de 2 integers worden ingesteld. Weet iemand misschien waarom deze code geweigerd wordt, en hoe ik die integers als argumenten meegeef?
Probeer het eens als volgt:

Java:
1
2
3
4
5
6
7
8
9
10
11
String message =
    "/live/clip/view" // address
    + "\0" // address terminator
    + "," // separator
    + "ii" // arguments
    + "\0\0\0\0" // arg 1 (int32, dus 4 bytes)
    + "\0\0\0\0" // arg 2 (int32, dus 4 bytes)
    + "\0\0\0" // padding (aanvullen tot veelvoud 4)
    ;
    
byte[] bytes = message.getBytes("ASCII");


In het voorbeeld dat je gaf neem je aan dat een int32 slechts één byte is. Verder zie ik je ook niet aanvullen (slechts 21 bytes) tot een veelvoud van 4, zoals eerder genoemd is. Let ook op dat er al verschillende libraries beschikbaar zijn, dus opzich hoef je het wiel dan niet opnieuw uit te vinden.

[ Voor 0% gewijzigd door Feanathiel op 29-04-2012 14:42 . Reden: String met hoofdletter. Ben gewend aan een .NET omgeving. ]


Acties:
  • 0 Henk 'm!

  • Marcj
  • Registratie: November 2000
  • Laatst online: 14-10 16:44
Ik zou hiervoor niet zelf een byte[] maken om te versturen, maar de DataOutputStream. Hiermee kun je iets als het onderstaande doen:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
DataOutputStream dos = new DataOutputStream(output);
dos.writeString("/live/clip/view"); // Address
dos.write(0); // address terminator
dos.writeChar(','); // separator
dos.writeString("ii"); // arguments
dos.writeInt(0); // argument 1, wordt geschreven als 4 bytes
dos.writeInt(0); // argument 2, idem
dos.write(0); // ten minste 1 padding byte nodig
while((dos.size()  & 3) == 0) {
    // voeg extra padding toe totdat je een veelvoud van 4 hebt
    dos.write(0);
}


Een aantal stukken van deze code zou je in een eigen implementatie van de DataOutputStream kunnen stoppen. Bijvoorbeeld:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OSCOutputStream extends DataOutputStream {
    public OSCOutputStream(OutputStream os) {
        super(os);
    }

    public void writeTerminatedString(String msg) {
        writeString(msg);
        write(0);
    }

    public void writePadding() {
        write(0);
        while((size() & 3) == 0) {
            write(0);
        }
    }
}


Dan wordt de code nog korter:

Java:
1
2
3
4
5
6
7
OSCOutputStream oos = new OSCOutputStream(output);
oos.writeTerminatedString("/live/clip/view"); // Address
oos.writeChar(','); // separator
oos.writeString("ii"); // arguments
oos.writeInt(0); // argument 1, wordt geschreven als 4 bytes
oos.writeInt(0); // argument 2, idem
oos.writePadding(); // add padding


Dit maakt het gelijk herbruikbaar :)

Waarschuwing: ongeteste code, heb het niet eens gecompileerd, maar het moet je wel een idee geven.

Acties:
  • 0 Henk 'm!

  • Herko_ter_Horst
  • Registratie: November 2002
  • Niet online
DataOutputStream heeft alleen zin als de ontvangende kant een DataInputStream gebruikt. Lijkt me hier niet van toepassing.

[ Voor 52% gewijzigd door Herko_ter_Horst op 29-04-2012 21:09 ]

"Any sufficiently advanced technology is indistinguishable from magic."


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 14-10 10:34
Herko_ter_Horst schreef op zondag 29 april 2012 @ 21:07:
DataOutputStream heeft alleen zin als de ontvangende kant een DataInputStream gebruikt. Lijkt me hier niet van toepassing.
Dit is onjuist, deze class is juist bedoeld om op een portable manier te communiceren. Het schrijft dingen gewoon als bytes weg en daar is niets Java-specifieks aan. Misschien ben je verward met de ObjectOutputStream.

Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
Herko_ter_Horst schreef op zondag 29 april 2012 @ 21:07:
DataOutputStream heeft alleen zin als de ontvangende kant een DataInputStream gebruikt. Lijkt me hier niet van toepassing.
DataOutputStream gebruikt XDR (eXternal Data Representation), en als de andere kant ook XDR spreekt, dan kan je prima een DataOutputStream gebruiken.

Sterker nog, die beschrijving van OSC lijkt erg op XDR.

[ Voor 7% gewijzigd door Remus op 30-04-2012 09:18 ]


Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
Marcj schreef op zondag 29 april 2012 @ 20:33:
Ik zou hiervoor niet zelf een byte[] maken om te versturen, maar de DataOutputStream. Hiermee kun je iets als het onderstaande doen:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
DataOutputStream dos = new DataOutputStream(output);
dos.writeString("/live/clip/view"); // Address
dos.write(0); // address terminator
dos.writeChar(','); // separator
dos.writeString("ii"); // arguments
dos.writeInt(0); // argument 1, wordt geschreven als 4 bytes
dos.writeInt(0); // argument 2, idem
dos.write(0); // ten minste 1 padding byte nodig
while((dos.size()  & 3) == 0) {
    // voeg extra padding toe totdat je een veelvoud van 4 hebt
    dos.write(0);
}


Een aantal stukken van deze code zou je in een eigen implementatie van de DataOutputStream kunnen stoppen. Bijvoorbeeld:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OSCOutputStream extends DataOutputStream {
    public OSCOutputStream(OutputStream os) {
        super(os);
    }

    public void writeTerminatedString(String msg) {
        writeString(msg);
        write(0);
    }

    public void writePadding() {
        write(0);
        while((size() & 3) == 0) {
            write(0);
        }
    }
}


Dan wordt de code nog korter:

Java:
1
2
3
4
5
6
7
OSCOutputStream oos = new OSCOutputStream(output);
oos.writeTerminatedString("/live/clip/view"); // Address
oos.writeChar(','); // separator
oos.writeString("ii"); // arguments
oos.writeInt(0); // argument 1, wordt geschreven als 4 bytes
oos.writeInt(0); // argument 2, idem
oos.writePadding(); // add padding


Dit maakt het gelijk herbruikbaar :)

Waarschuwing: ongeteste code, heb het niet eens gecompileerd, maar het moet je wel een idee geven.
De padding moet niet aan het einde van het bericht, maar aan het einde van ieder data element dat niet (MOD 4 byte = 0) is.

Acties:
  • 0 Henk 'm!

  • Marcj
  • Registratie: November 2000
  • Laatst online: 14-10 16:44
Remus schreef op maandag 30 april 2012 @ 09:19:
[...]


De padding moet niet aan het einde van het bericht, maar aan het einde van ieder data element dat niet (MOD 4 byte = 0) is.
Ok, ik weet duidelijk niet voldoende van het OSC protocol. Maar het idee is toch duidelijk?

Daarnaast ben ik wel met Feanathiel eens dat je ook wel zou kunnen kijken naar standaard libraries die dit soort generieke implementaties al gedaan hebben.
Pagina: 1