C# - Problema en transferencia de datos con Sockets

Iniciado por _LooSeR_, 12 Marzo 2010, 21:42 PM

0 Miembros y 2 Visitantes están viendo este tema.

_LooSeR_

Hola muy buenas!

Estoy desarrollando una aplicación cliente - servidor basada en sockets con Visual C# 2008 Express.

En una parte de la aplicación, quiero hacer un explorer muy básico, para poder navegar por los directorios del servidor y ver sus archivos.

Desde el servidor creo un Array de Strings, que contiene la ruta completa de los Directorios, o los Ficheros que están en una Ruta indicada.

Como desde el cliente no sé el tamaño de este Array, me envío antes un entero con la longitud de este, para luego poder recibirlo desde el cliente con un For.

El problema es que cuando recibo los "bytes" del entero en el cliente, no me llega un "10" por ejemplo, sino que me llega un "10C". Y evidentemente al pasar el string a int, me salta un error por que no tiene el formato adecuado.

Y aunque le pase la longitud bien (a mano), luego el cliente en el For que va leyendo las rutas que envía el servidor, las omite por completo, termina el método, y vuelve a obtener la longitud para otro listBox con los Archivos. Pero esta vez, para esta longitud, recibe los bytes del String "Ar" (Supongo que de "Archivos de programa", de los directorios que los había omitido anteriormente)

Estos son los códigos conflictivos:

Servidor

       private void ManageExplorer()
       {
           try
           {
               count = cliente.Receive(bytes);
               String miRuta = Encoding.ASCII.GetString(bytes, 0, count);

               if ((miRuta.Equals("")) || (miRuta==null))
                   miRuta = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory());

               enviar_carpetas(miRuta);
               enviar_archivos(miRuta);
           }
           catch (SocketException) { }
       }

       private void enviar_carpetas(String ruta)
       {
           try
           {
               string[] carpetas = Directory.GetDirectories(ruta);

               //Enviar data con la longitud del array
               int longitud = carpetas.Length;
               bytes = Encoding.ASCII.GetBytes(longitud.ToString());
               cliente.Send(bytes);

               foreach (String carpeta in carpetas)
               {
                   //Enviar data de cada carpeta
                   bytes = Encoding.ASCII.GetBytes(carpeta);
                   cliente.Send(bytes);
               }
           }
           catch (SocketException){}
       }

       private void enviar_archivos(String ruta)
       {
           try
           {
               string[] archivos = Directory.GetFiles(ruta);

               //Enviar data con la longitud del array
               int longitud = archivos.Length;
               bytes = Encoding.ASCII.GetBytes(longitud.ToString());
               cliente.Send(bytes);

               foreach (String archivo in archivos)
               {
                   //Enviar data de cada archivo
                   bytes = Encoding.ASCII.GetBytes(archivo);
                   cliente.Send(bytes);
               }
           }
           catch (SocketException){}
       }




Cliente
       public void RunExplorer(String ruta, ListBox listBoxCarpetas, ListBox listBoxArchivos)
       {
           try
           {
               bytes = Encoding.ASCII.GetBytes("RunExplorer");
               servidor.Send(bytes);

               if (!ruta.Equals("") && ruta != null)
                   bytes = Encoding.ASCII.GetBytes(ruta);
               else
                   bytes = Encoding.ASCII.GetBytes("C:\\");

               servidor.Send(bytes);

               //Ahora recibimos los listados de carpetas y archivos
               RecibirDatosExplorer(listBoxCarpetas);
               RecibirDatosExplorer(listBoxArchivos);
           }
           catch (SocketException) { }
       }

       private void RecibirDatosExplorer(ListBox listBox)
       {
           try
           {
               //Recogemos la longitud del array
               count = servidor.Receive(bytes);
               data = Encoding.ASCII.GetString(bytes, 0, count);
               log.AppendText(data + "\n");
               int longitud = int.Parse(data);
               
               listBox.Items.Clear();
               
               //Recogemos tantos datos como la longitud del array
               for (int i = 0; i < longitud; i++)
               {
                   count = servidor.Receive(bytes);
                   listBox.Items.Add(Encoding.ASCII.GetString(bytes, 0 ,count));
               }
           }
           catch (SocketException) { }
       }



Todas las otras funciones que había puesto hasta ahora en mi programa, apenas tenían el envío de 2 paquetes de bytes desde el cliente al servidor.

Mi duda es si al hacer un Receive el programa se queda esperando, o si no tiene nada pasa de largo.

Si existe algún problema al ser ambos los que reciben y envían tan seguido.

Por que no entiendo como me salta el bucle y no me actualiza el listBox, ya que no me muestra nada.
Y porque recibo "10C" en la longitud en lugar de la longitud bien.

Un saludo y muchas gracias ;)

[D4N93R]

Hola,
Bueno, una pregunta: Tienes que hacerlo con sockets? por qué no utilizas TcpListener/TcpClient con NetworkStream y un StreamReader/StreamWriter ...  así te ahorras un poco de trabajo...

En este tuto, que acabo de publicar, puedes ver como utilizar todas las clases que te dije.
http://foro.elhacker.net/net/tutorial_tcpip_sockets_c-t287407.0.html;new


De paso échale un ojo a este tutorial para que le agregues un toque de Threading, si es que ya no lo has hecho, claro está!

http://foro.elhacker.net/net/tutorial_iquestsystemthreading_iexclcomo_y_cuando_c-t277825.0.html

Espero te sirva de ayuda, sino, seguiremos intentándolo.

Saludos!

_LooSeR_

Ok muchas gracias ;)

La verdad había Leido algo de eso, pero encontré más informacion sobre sockets y lo intente así.

Con C# he empezado hace poco, ya que en la uni de lenguajes usados actualmente, prácticamente solo he llegado a ver Java... Di que no resulta dificil pasar de uno a otro

Un saludo y ya te comento!

[D4N93R]

Excelente, y bueno, fue muy buena tu decisión el pasarte a C# :D

Espero tu comentario..

raul338

Mira, la verdad soy fanatico de los sockets y los uso aun en .net :P (salvo algunas exepciones en las que si uso clases, pero cuando se tratan de proyectos personales, uso socket puro)

En cuanto a si se queda esperando, se queda esperando hasta que reciba al menos un paquete de bytes. Para que se quede esperando y puedas hacer alguna otra cosa, puedes usar los metodos asincronicos BeginReceibe, que funcionan como eventos, y saltan cuando reciben algun paquete de bytes.

En cuanto al tamaño "10C", no se porque sera así, deberias revisar el array de bytes antes del envio y apenas se recibe ("en crudo") pero mi recomendacion es que el tamaño te lo envies por bytes y no por strings. Si vas a recibir una cadena de 400 bytes, recibe antes 2 bytes con los valores (255, 145) que recibir el ascii de "400" (3 bytes)

Bueno, a simple vista esta bien hecho. Ahora vere si te hago un ejemplo con sockets puro, y un "sistema de comandos" para que trabajes mejor con sockets

_LooSeR_

#5
Bueno he estado un rato cambiando la aplicación, para tener la conexión con las clases que me dijiste.

El caso es que me salta una excepción de los sockets:

"Solo se permite un uso de cada dirección de socket (protocolo/dirección de red/puerto)"

Tanto por el lado del cliente como por el del servidor, si antes ya he iniciado el otro.

La verdad no se por qué... Ejecutando el servidor instrucción a instrucción, se me queda escuchando con el
tcpListener.Start();  
socketForServer = tcpListener.AcceptSocket();

Arranco el cliente instrucción a instrucción, y justo cuando crea la instancia del nuevo TcpClient, me salta esa excepción, como diciendo que ese socket ya está usado por el servidor y el cliente no puede usarlo...

Como es el principio del codigo, en el constructor del cliente y servidor os pongo lo que tengo hasta ahora:

Servidor:

Código (csharp) [Seleccionar]

               //Esto lo tengo porque me dice que el constructor TcpListener con sólo
               //el puerto como parámetro está obsoleto, y hay que meterle una
               //dirección, pero da problema de las dos formas, no es por esto.
               //IPAddress localipAddress = IPAddress.Parse("127.0.0.1");
               //TcpListener tcpListener = new TcpListener(localipAddress, port);

               TcpListener tcpListener = new TcpListener(port); //port = 25600
               tcpListener.Start();

               log.AppendText("Esperando conexión del cliente...\n");
               
               socketForClient = tcpListener.AcceptSocket();
               //Tras intentar ejecutar esta instrucción, si el cliente había sido ejecutado,
               //salta la excepción



Cliente:
Código (csharp) [Seleccionar]

               IPAddress direc = IPAddress.Parse("127.0.0.1");
               IPEndPoint IpEp = new IPEndPoint(direc, 25600);

               tcpClient = new TcpClient(IpEp);
               //Tras ejecutar esta instrucción salta la excepción si el server se había ejecutado antes.

               while (!tcpClient.Connected)
               {
                   System.Threading.Thread.Sleep(5000);
                   tcpClient.Close();
                   tcpClient = new TcpClient(IpEp);
               }


El while del cliente lo tengo para que siga intentando conectar, con sockets básicos era más sencillo, aqui me saltaba un error por tener ya abierto el tcpServer, también había probado en el bucle con tcpServer.Connect(IpEp) pero como tenia errores, he optado por cerrarlo y volver a crearlo, para que no de problemas con el problema que tengo en general.


Espero que sea una tonteria jeje, que el programa ya lo tengo cambiado del todo, y quiero ver si no me da el error antiguo que tenía al obtener el "int" de la longitud del array.


Gracias!


_LooSeR_

Emmm arreglado...

He cambiado en la parte del cliente,

En vez de hacer un IpEndPoint con (ipaddress, puerto) y luego pasárselo al constructor de TcpClient

He puesto como tenías en el tutorial, nombre del host ("localhost") y puerto, y me ha conectado... Lo que no sé porqué, ya que se supone que el constructor también admite el IpEndPoint


El caso es que nada más ver que conectaba, me he metido a ver mi explorador, y funciona, pasa correctamente los parametros enteros, para las longitudes de los array.

Y me muestra las carpetas y archivos perfectamente.

_LooSeR_

Tengo una duda ahora, para enviar un fichero.

He visto ejemplos con sockets, pero no he encontrado nada con estas clases.

Hay alguna forma sencilla de hacerlo?

Si no, he visto que desde el objeto TcpClient, su atributo client es un socket, ya que en realidad por lo visto estas clases utilizan los sockets normales, pero con sus métodos te hacen algo más sencillo el trabajo.

Si no se pudiera, cogiendo ese socket "client" podría usar el método SendFile. Pero me gustaría saber si sabéis de otra forma.

Un saludo!

[D4N93R]

Para mandar un archivo por un socket soo tienes que tener el Stream del mismo y enviarlo utilizando Network Stream :D

Pero Claro, tienes que construir una especie de protocolo para enviar los archivos y sus metadatas. Ejm:

envias esto: [FileName]Documento.doc
                  [FileLength]52
y luego esto [FileData]<binario del archivo>

Algo asi .. :D

_LooSeR_

Ok, voy a echarle un vistazo. Es que con esto de los streams y tal ando un poco perdido xD

Entonces tendría que abrirme el fichero con un FileStream, hacerme un StreamReader con el stream del fichero para poder leer los datos de el. no?

Pero sería un BinaryReader, no? ya que según he leido el otro lee strings solamente.

Y luego esos datos obtenidos tendría que enviarlos a traves del network stream, con otro BinaryWriter asociado al NetworkStream?

Aver si he cogido la idea...  :rolleyes:

Saludos!