Ayuda sobre Sockets. Enviar mensajes en hexadecimal java

Muy buenas tardes, necesito ayuda sobre sockets.
Actualmente tengo una aplicación socket cliente que se comunica con un socket servidor. hay intercambio de datos sin problema.

la pregunta es que
A principio de cada mensaje debe de contener la longitud del mensaje en hexadecimal:
mas o menos así:

7MENSAJE,

PERO EL NUMERO 7 DEBE SER EN HEXADECIMAL CONCATENANDO CON EL MENSAJE EN STRING

private void enviarDatos() {
try {
String mensaje="MENSAJE";
short tamanyo=7;/*ESTE DEBE SER EN HEXADECIMAL*/
String mensje2=tamanyo+mensaje;
salida.write(mensaje.getBytes());
salida.flush();
} catch (IOException ioe) {
ioe.getMessage();
}
}

AYUDENME PORFA

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de checotaku

Conversion entre bases

Si quieres obtener la representacion Hexadecimal como una cadena de un entero puedes usar el metodo toHexString(int x) de la clase Integer

String mensje2=Integer.toHexString(tamanyo)+mensaje;

Si la conversion entre bases es parte del problema que estas resolviendo , entonces talves necesites programar un metodo que realice la conversion de base 10 a cualquier otra base.

AYUDENM POR FA

QUIERO HACER UN PROGRAMA EN JAVA Q SEA SOBRE LOS CODIGOS DE MORSEN Y ME TIENEN Q HACER UN SONIDO DE CADA COMPU ALGIEN ME PUEDE AYUDAR O DAR UNA IDEA POR DOND COMENSAR GRACIAS SE LO AGRADESCO

Imagen de ezamudio

cuántos bytes?

Un ejemplo de encabezado de longitud en binario, a 2 bytes

http://www.javamexico.org/blogs/ezamudio/ejemplo_de_ataque_de_negacion_d...

Asi es, ya lo he visto, pero

Asi es, ya lo he visto, pero la conversión te lo hace en String y lo que yo necesito es
en bytes. Algo asi:
variable=0xb7;
Es decir poner como prefijo al mensaje.
Y si hago como lo dices sería asi:
String mensaje="MENSAJE";
String mensje2=Integer.toHexString(7);
String enviar=mensje2+mensaje;
PERO entonces, ya no se enviaría como hexadecimal(2 bytes), sino un string normal.

public static final char STX = '\u0002';
public static final char ETX = '\u0003';
ya lo he hecho con otros caracteres como el del inicio y el término de una cadena:
String mensaje="MENSAJE";
String mensjefinal=STX+mensaje+ETX;
Sólo que en lugar de STX, hay que representar la longitud en hexadecimal de 2 bytes y no hexadecimal en String.

Sucede que al final en es el mismo número

Pero tu problema es que lo estás queriendo meter en otra parte del protocolo:

Mira:

int hex = 0xF; // en hexa decimal
y
int dec = 15; // en decimal

Son exactamente el mismo numero:

import static java.lang.System.out;
public class AsHex{
    public static void main( String [] args ){
        int hex = 0xF;
        int dec = 15;
        out.println( "hex == dec ? " + (hex == dec)); // imprime true
       
    }
}

Al parecer tu problema esta en que no sabes como enviar este número por el socket y piensas que concatenandoselo al mensaje funcionará. Nope!, así no va a jalar.

Si se lo concatenas al mensaje, entonces tu mensaje será "7MENSAJE" y aún te hará falta mandar el encabezado ( y de hecho la longitud sería: 8 por el caracter 7 extra que le metiste )

Usar "0x7MENSAJE" no arregla en nada el asunto por que como ya te diste cuenta, le estás concatenando "caracteres" al mensaje y no mandando la longitud.

Lo único que tienes que hacer ( si entendí bien tu protocolo ) es mandarle primero la longitud y luego el mensaje:

String mensaje = "MENSAJE";
byte length  = ( byte )  mensaje.length();
out.write( length ); // escribe 7 en hexadecimal ( o en decimal o en realidad NO importa por que
// por debajo escribe escribe algo como 00000111 en binario )
// y luego el mensaje tal cual
out.write( mensaje.getBytes() );

De esta forma el otro lado del socket lo primerititito que va a leer va a ser un entero ( o un byte con muchos ceros y tres 1's al final )

Y luego va a leer un bonche de bytes de los cuales los primero 7 serán el mensaje.

edit

La explicación de por que concatenar STX y ETX te funciona es la siguiente

Al flujo de bytes que viaja por la red esto le importa un cacahuate, el solo ve un montón de bytes que van por ahí viajando.

Cuando haces:

public static final char STX = '\u0002';
public static final char ETX = '\u0003';
String mensjefinal=STX+mensaje+ETX;

Y lo envías, estás en realidad enviando un byte que representa el 2 ( \u0002 ) y luego los bytes del mensaje, pero esta mal.

Si quieres hacer lo mismo tendrías que hacer lo siguiente:

String mensaje = "MENSAJE";
char length = (int) mensaje.length();
out.write( length + mensaje );

Y te va a funcionar ( por que estas poniendo en el 7 como como el character unicode '\u0007' y al concatenarlo con el String le pone esos bytes ) ; pero no puedes seguir haciendo eso por que al hacerse más complicado el protocolo se te va a caer en pedazos horriblemente.

El mayor problema que se te puede presentar con esto, es que la cantidad de bits que lleva cada byte depende del "encoding" que lleva. Al parecer estas corriendo con suerte y cada caracter Java te lo está escribiendo como 8 bits, pero si la plataforma usa otro encoding te lo podria mandar de 16 bits, entonces te pondría 8 ceros extra y adios protocolo.

ejem. letra 'a' en UTF-8 = 01000001 en binario

Pero la letra 'a' en UTF-16 es: 0000000001000001

Es por eso que en Java 1.6 se añadio el método: String.getBytes( charset: Charset):byte[]

Para que tu como desarrollador tengas control de la forma en la que se van a representar esos bytes.

En conclusión tienes que llamarlo así:

public void sendMessage( String message,  OutputStream out ) {
      out.write( ( byte ) message.length() ); // mandar la longitud como un byte
      out.write( message.getBytes(Charset.forName("UTF-8"))); // mandar el mensaje usando UTF-8 como encoding
}

Espero que esto te sea útil.

En efecto asi es

la longitud del mensaje no se envía como encabezado, sino que parte del mensaje.
Lo que pasa es que estoy desarrollando una aplicación y no viene explicado especificamente.
y en el apartado solo dice así:

AL PRINCIPIO DE CADA MENSAJE DEBE CONTENER LOS CARACTERES EN HEXADECIMAL (2 BYTES) INDICANDO EL TAMAÑO DEL MENSAJE.

por lo que yo entiendo que no es el encabezado.

eso es lo que pide la documentación.

Saludos....

Si dice 2 bytes tienes que usar short entonces

out.write( ( short  ) message.length() );

Casualmente un char tambien tiene dos bytes, lo cual concuerda con aquello de char STX = '\u0002';

Lo mejor que puedes hacer, es ir probando.

Muchas gracias

De hecho lo manejé como short, pero como te comento, en la documentación decia que en hexadecimal.

Me causa confusión, si es así lo dejo como está, es decir como short.
Y lo que haré es tener 2 versiones, una donde maneje el short y la otra ocupando un arreglo de bytes
Gracias por todo...

Je eje es lo mismo.

Lo que pasa es que hexadecimal es una representación "humana" de un mismo número, al flujo de bytes le da lo mismo

0b1111111111 // 15 en binario
0o17               // 15 en octal
15                   // 15 en decimal
0xF                 // 15 en hexadecimal

Todos representan el mismo número. Pero bueno ya te irá cayendo el 20 poco a poco :) :) Lo bueno es que ya te está funcionando.

Para leerlo es igual, pero al revés :)

jejej te dije que se te iba a empezar a hacer pedazos el protocolo si lo querías hacer todo con un solo string.

Lee y relee lo que escribí hasta que lo entiendas.

Cuando lo hayas entendido, aplica lo mismo pero en vez de write( ( short ) length ) usa byte length = in.read();

El mensaje en realidad no te está llegando truncado ( es decir no es que primero te llegue uno y luego otro ) quizá sea tu interpretación por que lo estás queriendo ver en algún editor o algo así.

Si el mensaje llega truncado, no está cumpliendo con el protocolo y no debes de hacer nada por solucionarlo.

> Hablando de sockets, manejamos los mensajes como sigue:
> public static final char STX = '\u0002';
> public static final char ETX = '\u0003';
> String "MENSAJE"
>
> String mensajeEnviar=STX+MENSAJE+ETX;
>
> while (entrada.read()) {
> }
> Pues la lectura de lo que llega, se lee bien y todo.
> Mi duda es como le hago para leer el mensaje, cuando me llegan varios a la vez: algo asi:
> STX+MENSAJE+ETX+STX+MENSAJE+ETX+STX+MENSAJE+ETX
> porque si sucede en cuestionde milisegundos.
> como separar los mensajes indicando por STX y ETX, o cuando lleguen tramas como este:
> primera trama:
> STX+MENSAJE+ETX+STX+MEN
> segunda trama:
> SAJE+ETX+STX+MENSAJE+ETX
>
> armar la primera trama y luego la segunda. porque llegan cortadas
>
>

Ejemplo:

Aquí te va un ejemplo de como leer un protocolo muy sencillo que consiste en <message_size>+<message>* repetido n veces:

import java.io.*;
import java.net.*;
import static java.lang.System.out;

class MiniServer {
    public static void main( String [] args ) throws IOException {
        MiniServer server = new MiniServer(8082);
    }
    public MiniServer( int port ) throws IOException {
        // Create server on port 8082
        ServerSocket server = new ServerSocket(8082);
        // Take the first client
        Socket client = server.accept();
        // handle it
        handleClient( client );
        // and die
        client.close();
        server.close();
    }
    private void handleClient( Socket client ) throws IOException {
        // The protocol is:
        // length + message until lenght is < 0 or connection is closed
        short length = 0;
        InputStream is = client.getInputStream();
        // The first thing we get is the size
        while( ( length = (short)is.read() ) > 0 ) {
           
            // we print how many byte to read:
            out.printf("message length 0x%x bytes[", length);

            // we create a buffer of that size to hold the message
            byte[] buffer = new byte[length];// read "length" bytes

            // And we read n times
            for( int i = 0 ; i < length ; i++ ) {
                buffer[i] = (byte)is.read();
                out.printf( ",0x%x(%c)",buffer[i],buffer[i]);
            }
            // and we print the message
            out.printf("]; message \"%s\"%n", new String( buffer, "UTF-8"));
        }
       
    }
}
class MiniServerClient {
    private final Socket client;
    private final OutputStream out;
   
    public static void main( String [] args ) throws IOException {
        // Connect to the server
        MiniServerClient client = new MiniServerClient(8082);
        // Send the strings received as args:
        for( String message : args ){
            client.send( message );
        }
        // and close
        client.close();
    }
    public MiniServerClient( int serverPort ) throws IOException {
        client = new Socket( "localhost", serverPort );
        out = client.getOutputStream();
    }
    // Sends a message with the protocol: size in hex + byte array
    public void send( String message  ) throws IOException {
        out.write( ( short)  message.length() );
        out.write( message.getBytes("UTF-8"));
    }
    public void close() throws IOException {
        out.close();
        client.close();
    }
}

Corrida de ejemplo:

$java MiniServer &
$
$java MiniServerClient Hello sockets world!
message length 0x5 bytes[,0x48(H),0x65(e),0x6c(l),0x6c(l),0x6f(o)]; message "Hello"
message length 0x7 bytes[,0x73(s),0x6f(o),0x63(c),0x6b(k),0x65(e),0x74(t),0x73(s)]; message "sockets"
message length 0x6 bytes[,0x77(w),0x6f(o),0x72(r),0x6c(l),0x64(d),0x21(!)]; message "world!"
$
$
$
$java MiniServer &
$
$java MiniServerClient "Hello sockets world"
$message length 0x13 bytes[,0x48(H),0x65(e),0x6c(l),0x6c(l),0x6f(o),0x20( ),0x73(s),0x6f(o),0x63(c),0x6b(k),0x65(e),0x74(t),0x73(s),0x20( ),0x77(w),0x6f(o),0x72(r),0x6c(l),0x64(d)]; message "Hello sockets world"

Si te fijas en el segundo ejemplo el tamaño del mensaje es 0x13 Pero eso es solo el formato con el que se imprime "message length 0x%x"

Cuando se escribio se fue derechito como un numero nomás: out.write( ( short)  message.length() );

Este protocolo dice: "dime de que tamaño es el mensaje y yo lo voy a leer" Es por eso que cuando se procesa solamente dice:

while( ( length = (short)is.read() ) > 0 ) {
   ...
    for( int i = 0 ; i < length ; i++ ) {
        buffer[i] = (byte)is.read();
    }
}

Es decir; lo primero que lee es un "short" con el numero a crear y luego lee esa cantidad de veces y ya, no hace más intento por descifrar el mensaje.

Claro está, un protocolo que valga la pena es mucho más complicado que esto, pero estas son las bases.

Espero que te sirva.

Acá hay otro ejemplo de un protocolo ligeramente más complicado:
http://java.sun.com/docs/books/tutorial/networking/sockets/clientServer....