none
array de byte[] con TcpClient o Socket RRS feed

  • Pregunta

  • Hola.

    Estoy intentando hacer un programa que se conecte a un webchat con la clase TcpClient o la clase Socket. Las mismas reciben datos en un array de byte[]. En los ejemplos que he visto por ahí, crean el array con 256 posiciones: "byte[] elbuffer = new byte[256];". Hasta ahí todo bien. La cuestión es que si hago eso en mi programa, las líneas no llegan perfectamente y se visualizan cortadas. De hecho, mientras más reduzca el número de posiciones del array, más lineas se ven bien y menos líneas cortadas salen. Si pongo el array con sólo 1 posicion (reemplazando el 256 por el 1), el texto llega perfecto, pero ello también implica que el texto sea procesado de una manera exajeradamente lenta.

    Si alguien tiene idea de por qué sucede esto, le agradezco lo comente en este hilo, ya que llevo 2 días intentando conseguir algo y no logro nada :-( Añadir que he probado varias maneras de recibir los datos, y esa es una de las que encontré en la MSDN. Todas ellas con el mismo resultado...

    Ahora pondré el código. Si te ves motivado intenta probarlo y verás de lo que hablo. El código está comentado para que comprendais qué es lo que cambio. Añade un textbox multiline para que puedas ver el texto recibido. De antemano gracias por dedicarle tiempo ;-)

    Code Snippet

    //Globales
    NetworkStream elstream;
    TcpClient cliente;
    Thread elthread;

    //boton para conectar
    private void buttonConectar_Click(object sender, EventArgs e)
    {
      cliente = new TcpClient("213.4.130.190", 6969);
                elstream = cliente.GetStream();
                //inicia thread
                elthread = new Thread(new ThreadStart(recibe));
                elthread.Start();

                //envia login
                enviar("PASS%20NULL");
                enviar("NICK nickdeprueba");
                enviar("user A21 - - chatFlashXML");
    }

    //metodo llamado desde thread que recibe texto del socket
    void recibe()
            {
                while (true)
                {
                    if ((elstream.CanRead) && (elstream.DataAvailable))
                    {
                        try
                        {
                            byte[] elbuffer = new byte[256];
        //si en vez de la linea anterior, ponemos: "byte[] elbuffer = new byte[1];", el texto llegará correctamente, pero de una manera demasiado lenta. Conforme vayamos descendiendo el número de posiciones del array, más caracteres llegarán. Sin duda algo rarísimo..... :-/
                            int i = 0;
                            do
                            {
                                i = elstream.Read(elbuffer,0,elbuffer.Length);
    //"textBoxDebug" es un textbox multiline
                                textBoxDebug.AppendText(Encoding.UTF8.GetString(elbuffer));
                            } while (i > 0);

                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Excepcion al recibir: " + ex.Message);
                        }
                    }
                }
            }


    //metodo para enviar informacion al socket:
    void enviar(string mensaje)
            {
                if (elstream.CanWrite) {
                    try {
                        mensaje = "<raw>"+mensaje+"</raw>\r\n";
                        byte[] elbuffer = Encoding.UTF8.GetBytes(mensaje);
                        elstream.Write(elbuffer,0,elbuffer.Length);
                    }
                    catch (Exception ex) { MessageBox.Show("Error al enviar: " + ex.Message); }
                }
            }




    viernes, 25 de enero de 2008 2:15

Respuestas

  • Hola chabu, a mi estas clases, la TcpClient y Socket también me traen por la calle de la amargura. He mirado tu ejemplo y no se me ocurre nada, lo único que he encontrado (y no creo que tenga mucho sentido) es que en MSDN utilizan Byte (con la b en mayuscula) en vez de byte, aunque parecen ser iguales. Yo tengo otra duda, no se si tu me podras ayudar. Veras, si yo quisiera leer un fichero de texto alojado en, por ejemplo, "213.4.130.190/carpeta/f.txt" con TcpClient o Sockets ¿cómo lo haria? es que en cuanto le añado algo a la ip me salta la excepcion "host desconocido". Aunque para esto hay otros metodos mas sencillos que funcionan perfectamente (WebRequest y StreamReader por ejemplo) quiero hacerlo con alguna de estas dos clases para practicar un poco y despues intentar leer streamings mp3 (shoutcast). Ya estoy dudando hasta de si esto se puede hacer con estas clases... aqui tienes el post que dejé hace unos días: http://forums.microsoft.com/MSDN-ES/ShowPost.aspx?PostID=2732349&SiteID=11

    viernes, 25 de enero de 2008 13:12
  • es bastante lógico lo que sucede.

    en primer lugar estás creando un byte[]

    a ese byte array le estás cargando tanta información como el tamaño mismo del array.

    luego inicializas la variable contadora i para correr un loop hasta que ya no leas datos.

     

    ahora bien, supongamos que tienes 100 bytes en el array.

     

    la primera vuelta del loop entras y lees todo el contenido del socket pues cabe en tu array. al mismo tiempo i pasa a valer 100.

    concatenas ese contenido a tu string.

    evalúas i que es mayor que cero y por tanto corres de nuevo el loop.

    vuelves a leer el socket. no hay nada disponible por tanto i pasa a valer 0.

    concatenas nuevaemente el contenido que el array de bytes (el buffer) todavía tiene desde la pasada anterior por el loop a tu string.

    evalúas i que es igual a 0 y sales del loop.

     

    otro error es que cada vez que estás imprimiendo el contendio del buffer imprimes todo el contenido, sin importar cuánto leíste. si el socket tuviera 300 bytes lo que sucedería sería que concatenarías al string los primeros 256, luego los 100 restantes + los 156 últimos bytes de la primera vuelta y finalmente volverías a concatenar lo que concatenaste en la segunda vuelta.

    por poner un ejemplo, si el buffer fuera de dos bytes y el stream contuviera tres "ABC", estarías concatenando:

     

    ABCBCB.

     

    tienes que tener en cuenta que el buffer no es de tamaño dinámico (cuando vuelves a llenar comienza desde la posición 0 hasta donde haya datos y luego deja lo que ya estaba) y que cuando invocas el GetBytes() y le pasas el buffer toma todo lo que él tenga (no sabe cuánto fue lo que pusiste la última vez.

     

    te paso dos tips. uno, reinicializa el buffer en cada corrida. dos, convierte el bloque do/while en otra sentencia de control.

     

    Code Snippet

    while (elstream.DataAvailable)   // checkeo que haya data en el socket

    {

    elbuffer.Initialize();          // para borrar el contenido anterior

    // o simplemente mueve el byte[] elbuffer = new byte[256]; aquí

     

    i = elstream.Read(elbuffer, 0, elbuffer.Length);

    if (i > 0)             // ahora también me aseguro de haber leído data del socket

    {

    // usa el contenido del buffer

    }

    }

     

     

    de ese modo sólo vas a ingresar al loop cuando haya realmente datos y vas a tener un buffer con el contenido mínimo indispensable.

    viernes, 1 de febrero de 2008 18:21

Todas las respuestas

  • Hola chabu, a mi estas clases, la TcpClient y Socket también me traen por la calle de la amargura. He mirado tu ejemplo y no se me ocurre nada, lo único que he encontrado (y no creo que tenga mucho sentido) es que en MSDN utilizan Byte (con la b en mayuscula) en vez de byte, aunque parecen ser iguales. Yo tengo otra duda, no se si tu me podras ayudar. Veras, si yo quisiera leer un fichero de texto alojado en, por ejemplo, "213.4.130.190/carpeta/f.txt" con TcpClient o Sockets ¿cómo lo haria? es que en cuanto le añado algo a la ip me salta la excepcion "host desconocido". Aunque para esto hay otros metodos mas sencillos que funcionan perfectamente (WebRequest y StreamReader por ejemplo) quiero hacerlo con alguna de estas dos clases para practicar un poco y despues intentar leer streamings mp3 (shoutcast). Ya estoy dudando hasta de si esto se puede hacer con estas clases... aqui tienes el post que dejé hace unos días: http://forums.microsoft.com/MSDN-ES/ShowPost.aspx?PostID=2732349&SiteID=11

    viernes, 25 de enero de 2008 13:12
  • Hola.

     

    Gracias por tu intento... Lamentablemente lo de "Byte" o "byte" no es el problema. :-S

     

    Ya te respondí a tu pregunta en tu hilo.

     

    Un saludo y gracias.

    viernes, 25 de enero de 2008 13:47
  • Me parece que el detalla hay es que el buffer para leer lo estas creando de un determinado tamaño, debes crearlo de acuerdo a lo que obtengas del stream, checa el tamaño de bytes que obtienes del flujo y en base a ese creas tu buffer.

    Espero te sea de ayuda

    Recuerda marcar la respuesta si te fue de utilidad
    viernes, 1 de febrero de 2008 16:42
  • es bastante lógico lo que sucede.

    en primer lugar estás creando un byte[]

    a ese byte array le estás cargando tanta información como el tamaño mismo del array.

    luego inicializas la variable contadora i para correr un loop hasta que ya no leas datos.

     

    ahora bien, supongamos que tienes 100 bytes en el array.

     

    la primera vuelta del loop entras y lees todo el contenido del socket pues cabe en tu array. al mismo tiempo i pasa a valer 100.

    concatenas ese contenido a tu string.

    evalúas i que es mayor que cero y por tanto corres de nuevo el loop.

    vuelves a leer el socket. no hay nada disponible por tanto i pasa a valer 0.

    concatenas nuevaemente el contenido que el array de bytes (el buffer) todavía tiene desde la pasada anterior por el loop a tu string.

    evalúas i que es igual a 0 y sales del loop.

     

    otro error es que cada vez que estás imprimiendo el contendio del buffer imprimes todo el contenido, sin importar cuánto leíste. si el socket tuviera 300 bytes lo que sucedería sería que concatenarías al string los primeros 256, luego los 100 restantes + los 156 últimos bytes de la primera vuelta y finalmente volverías a concatenar lo que concatenaste en la segunda vuelta.

    por poner un ejemplo, si el buffer fuera de dos bytes y el stream contuviera tres "ABC", estarías concatenando:

     

    ABCBCB.

     

    tienes que tener en cuenta que el buffer no es de tamaño dinámico (cuando vuelves a llenar comienza desde la posición 0 hasta donde haya datos y luego deja lo que ya estaba) y que cuando invocas el GetBytes() y le pasas el buffer toma todo lo que él tenga (no sabe cuánto fue lo que pusiste la última vez.

     

    te paso dos tips. uno, reinicializa el buffer en cada corrida. dos, convierte el bloque do/while en otra sentencia de control.

     

    Code Snippet

    while (elstream.DataAvailable)   // checkeo que haya data en el socket

    {

    elbuffer.Initialize();          // para borrar el contenido anterior

    // o simplemente mueve el byte[] elbuffer = new byte[256]; aquí

     

    i = elstream.Read(elbuffer, 0, elbuffer.Length);

    if (i > 0)             // ahora también me aseguro de haber leído data del socket

    {

    // usa el contenido del buffer

    }

    }

     

     

    de ese modo sólo vas a ingresar al loop cuando haya realmente datos y vas a tener un buffer con el contenido mínimo indispensable.

    viernes, 1 de febrero de 2008 18:21
  • mira yo te recomiendo que leas los bytes exactos el motivo por el que no te sale el texto completo es que tienes el arreglo de bytes estimado en 256 podrias probar recortandolo cuando leas menos bytes del tamaño.

    while(i > 0)
    {
         i = elstream.Read(elbuffer,0,elbuffer.Length);

         //Validamos si el arreglo es mas pequeño de los leidos

        if(i<elbuffer.Lenght)

        {

            Array.Resize(ref elbuffer,i);

        }

         //"textBoxDebug" es un textbox multiline

         textBoxDebug.AppendText(Encoding.UTF8.GetString(elbuffer));

    De esta manera recortaras los ceros en el arreglo de bytes y convertiras solo el texto necesario.




    • Editado Urferu viernes, 6 de febrero de 2015 4:05
    viernes, 6 de febrero de 2015 4:03