PDA

Ver la Versión Completa : Error sockets entre server y cliente en android.


manolazo
27/12/13, 12:22:29
Buenas.

Intento establecer conexion entre un servidor creado en java Netbeans, en lineas generales en la clase del servidor:


ServerSocket server = new ServerSocket(puerto); //puerto = 30922;
while (true){
Socket client = server.accept();
System.out.println("Aceptado desde :"+client.getInetAddress());
ChatHandler c = new ChatHandler(client);
c.start();
}


donde en la clase ChatHandler para no extendernos el constructor seria:


public ChatHandler (Socket s) throws IOException{
this.s=s;
in = new DataInputStream(new BufferedInputStream(s.getInputStream()));
out = new DataOutputStream ( new BufferedOutputStream(s.getOutputStream()));
}


Por parte del ciente android a grandes rasgos en onCreate:


String ip = "192.168.1.2";
int puerto = 30922;
Log.v(this.getClass().getClass().getCanonicalName( ), "Socket " + ip + " " + puerto);
try {
sk = new Socket(ip, puerto);
Log.v(getClass().getCanonicalName(), "Se ha iniciado conexion...");
Log("Conexion con servidor: "+ip+" Puerto: "+puerto);
entrada = new DataInputStream(new BufferedInputStream(sk.getInputStream()));
salida = new DataOutputStream(new BufferedOutputStream(sk.getOutputStream()));

} catch (UnknownHostException e) {
e.printStackTrace();
salidaTxt.append(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
salidaTxt.append(e.getMessage());
}



Pero me salta la exception con error de conexion. Supongo que sera por las ips , pero no logro dar con ello.

Lo estoy haciendo con el avd de eclipse y supongo que la ip que tendre que poner "192.168.1.2" es la ip donde esta el pc?

ando un poco perdido.

Un saludo.

jtsamper
28/12/13, 09:32:44
Si, deberas meterle la ip de tu pc y el puerto por el que se comunican. Y fijate qe este corriendo el servidor antes de iniciar el cliente. Tambien comprueba que el avd te conecta a internet.

Pero creo qe el fallo esta en el servidoe porque no habre correctamente los sockets. Has comprobado si te salta errores en el servidor???

manolazo
28/12/13, 12:52:28
Hola.

El avd lo tengo conectado a internet porque he probado a abrir navegador en el avd y funciona.
El puerto esta abierto y la ip es la del pc.
El servidor esta corriendo lo abro desde netbeans pero no se si abre correctamente los sockets ya que el error salta cuando ejecuto la aplicacion cliente desde eclipse.

trazas:


12-28 11:45:53.281: E/AndroidRuntime(840): FATAL EXCEPTION: main
12-28 11:45:53.281: E/AndroidRuntime(840): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.socketclientprueba/com.example.socketclientprueba.SocketCliente}: android.os.NetworkOnMainThreadException
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.ActivityThread.performLaunchActivity(A ctivityThread.java:2180)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.ActivityThread.handleLaunchActivity(Ac tivityThread.java:2230)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.ActivityThread.access$600(ActivityThre ad.java:141)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.ActivityThread$H.handleMessage(Activit yThread.java:1234)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.os.Handler.dispatchMessage(Handler.java:99 )
12-28 11:45:53.281: E/AndroidRuntime(840): at android.os.Looper.loop(Looper.java:137)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.ActivityThread.main(ActivityThread.jav a:5041)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.lang.reflect.Method.invokeNative(Native Method)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.lang.reflect.Method.invoke(Method.java:511)
12-28 11:45:53.281: E/AndroidRuntime(840): at com.android.internal.os.ZygoteInit$MethodAndArgsCa ller.run(ZygoteInit.java:793)
12-28 11:45:53.281: E/AndroidRuntime(840): at com.android.internal.os.ZygoteInit.main(ZygoteInit .java:560)
12-28 11:45:53.281: E/AndroidRuntime(840): at dalvik.system.NativeStart.main(Native Method)
12-28 11:45:53.281: E/AndroidRuntime(840): Caused by: android.os.NetworkOnMainThreadException
12-28 11:45:53.281: E/AndroidRuntime(840): at android.os.StrictMode$AndroidBlockGuardPolicy.onNe twork(StrictMode.java:1117)
12-28 11:45:53.281: E/AndroidRuntime(840): at libcore.io.BlockGuardOs.connect(BlockGuardOs.java: 84)
12-28 11:45:53.281: E/AndroidRuntime(840): at libcore.io.IoBridge.connectErrno(IoBridge.java:127 )
12-28 11:45:53.281: E/AndroidRuntime(840): at libcore.io.IoBridge.connect(IoBridge.java:112)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.net.PlainSocketImpl.connect(PlainSocketImpl.j ava:192)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.net.PlainSocketImpl.connect(PlainSocketImpl.j ava:172)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.net.Socket.startupSocket(Socket.java:566)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.net.Socket.tryAllAddresses(Socket.java:127)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.net.Socket.<init>(Socket.java:177)
12-28 11:45:53.281: E/AndroidRuntime(840): at java.net.Socket.<init>(Socket.java:149)
12-28 11:45:53.281: E/AndroidRuntime(840): at com.example.socketclientprueba.SocketCliente.onCre ate(SocketCliente.java:72)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.Activity.performCreate(Activity.java:5 104)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.Instrumentation.callActivityOnCreate(I nstrumentation.java:1080)
12-28 11:45:53.281: E/AndroidRuntime(840): at android.app.ActivityThread.performLaunchActivity(A ctivityThread.java:2144)
12-28 11:45:53.281: E/AndroidRuntime(840): ... 11 more

jtsamper
28/12/13, 13:25:19
Mmmm tienes el permiso de internet en la aplicacion? Supongo que si.

Mete el hilo del cliente en un servicio, de esta forma no te dara problemas, y utilizar el IBinder para comunicarlo con la actividad.

Espero que sirva de ayuda.

Arasthel
28/12/13, 13:31:03
Trabajo asíncrono con red -> hilo aparte. Ponlo en un Thread o en el doInBackground de un AsyncTask y no reventará.

@jtsamper: meter el código en un service sólo para eso es un poco matar moscas a cañonazos, a no ser que tenga que estar funcionando incluso si la app no o que tenga que compartirse en varias activities. Además, si no recuerdo mal, el código de un service sigue ejecutándose en el hilo principal (al menos si está en un proceso aparte).

Un saludo.

manolazo
28/12/13, 15:22:45
Buenas.

Si, tengo puesto el permiso de internet en el Manifest:
<uses-permission android:name="android.permission.INTERNET"/>

Y aunque no lo haya puesto en el código, tambien esta creado un hilo aparte para recoger mensaje desde DataInputStream

El codigo del OnCreate del cliente:


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket_cliente);
texto = (EditText) findViewById(R.id.EditText01);
salidaTxt = (TextView) findViewById(R.id.TextView01);
contador = (TextView) findViewById(R.id.textView1);

btnEnviar = (Button) findViewById(R.id.button1);
btnEnviar.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {
Log.v(getClass().getCanonicalName(), "Iniciando Socket...");
Log("Enviando... " + texto.getText().toString() + "\n");
ejecutaClienteSocket();
}
});

String ip = "192.168.1.2";
int puerto = 30922;
Log.v(this.getClass().getClass().getCanonicalName( ), "Socket " + ip + " " + puerto);
try {
sk = new Socket(ip, puerto);
Log.v(getClass().getCanonicalName(), "Se ha iniciado conexion...");
Log("Conexion con servidor: "+ip+" Puerto: "+puerto);
entrada = new DataInputStream(new BufferedInputStream(sk.getInputStream()));
salida = new DataOutputStream(new BufferedOutputStream(sk.getOutputStream()));

} catch (UnknownHostException e) {
e.printStackTrace();
salidaTxt.append(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
salidaTxt.append(e.getMessage());
}


final Handler myHandler = new Handler();

(new Thread(new Runnable() {
public void run() {
while (true) {
String recibe;
try {
recibe = getEntrada().readUTF();
if (recibe != null) {
SocketCliente.this.setMsg(recibe);
Log.v(getClass().getCanonicalName(), "Recibiendo datos.." + recibe);
}

}catch (IOException e) {
e.printStackTrace();
}
myHandler.post(new Runnable() {
public void run() {
actualizarMensajes();
}
});
}
}
})).start();


Log.v(getClass().getCanonicalName(), "Terminando de iniciar componentes");
}


El handler es para mostrar en un textview el mensaje recibido.

Da la impresion de que es un error de conexion de sockets , no?

Arasthel
28/12/13, 16:23:05
La creación del socket la haces fuera del hilo, y tiene toda la pinta de ser eso lo que da fallo (al menos según el logcat). ¿Qué línea es la 72 en tu código?

12-28 11:45:53.281: E/AndroidRuntime(840): at com.example.socketclientprueba.SocketCliente.onCre ate(SocketCliente.java:72)

Un saludo.

kriogeN
28/12/13, 16:44:22
Como dice Arasthel, la creación de la conexión la haces en el hilo principal, y eso también hace uso de Internet (por eso el UnknownHostException). Hasta tal punto que es lo que más bloqueante puede ser (resolver el host, el host está caido, etc).

También debe hacerse fuera del hilo principal.

manolazo
29/12/13, 12:53:36
Hola de nuevo.
La linea 72 es efectivamente la creacion del socket: sk = new Socket(ip, puerto);

Pero poniendo la creacion de la conexion

try {
sk = new Socket(ip, puerto);
Log.v(getClass().getCanonicalName(), "Se ha iniciado conexion...");
Log("Conexion con servidor: "+ip+" Puerto: "+puerto);
entrada = new DataInputStream(new BufferedInputStream(sk.getInputStream()));
salida = new DataOutputStream(new BufferedOutputStream(sk.getOutputStream()));

} catch (UnknownHostException e) {
e.printStackTrace();
salidaTxt.append(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
salidaTxt.append(e.getMessage());
}
dentro del Thread tampoco funciona y da el mismo error.

Pero pasa lo siguiente:

Usando un device virtual basado en Android 2.3.3 si que funciona y se establece conexion pero usando un 4.2.2 no funciona. Ni poniendo la conexion dentro ni fuera del Thread.

Que puede haber que funcione en 2.3.3 y no en 4.2.2?? No he probado con demas versiones android.
Alguna idea?

Arasthel
29/12/13, 13:02:53
La NetworkOnMainThreadException es una excepción que ocurre sólo a partir de Android 3.0 y que sale porque algo que hace uso de red está en el hilo principal, así que algo tiene que haberse quedado fuera del Thread que hace uso del socket o de alguna forma de conexión. Mira el logcat, busca qué línea te dice de tu clase que está fallando (antes era la 72) y ponlo en un hilo aparte.

EDIT: ¿puedes poner el log y también el código tal y como está, a ver si por fin vemos dónde falla esto?

Un saludo.

neoadn
29/12/13, 13:26:39
Trabajo asíncrono con red -> hilo aparte. Ponlo en un Thread o en el doInBackground de un AsyncTask y no reventará.

@jtsamper: meter el código en un service sólo para eso es un poco matar moscas a cañonazos, a no ser que tenga que estar funcionando incluso si la app no o que tenga que compartirse en varias activities. Además, si no recuerdo mal, el código de un service sigue ejecutándose en el hilo principal (al menos si está en un proceso aparte).

Un saludo.

A mi me gusta mas un intentService. Ademas se puede comunicar con la app principal bastante fácil y no da tantos problemas al funcionar.

Supongo que para caso habrá lo suyo, pero el service me congelaba una activity y el intentservice no :D

manolazo
29/12/13, 15:09:42
La NetworkOnMainThreadException es una excepción que ocurre sólo a partir de Android 3.0 y que sale porque algo que hace uso de red está en el hilo principal, así que algo tiene que haberse quedado fuera del Thread que hace uso del socket o de alguna forma de conexión. Mira el logcat, busca qué línea te dice de tu clase que está fallando (antes era la 72) y ponlo en un hilo aparte.

EDIT: ¿puedes poner el log y también el código tal y como está, a ver si por fin vemos dónde falla esto?

Un saludo.

Hola, gracias por la aclaración. Ahora ya funciona, no sabia que a partir de la 3.0 tenia que estar todo en un hilo aparte todo lo concerniente a uso de red.

Un a vez puesto todo el un hilo aparte el problema era que dentro dle propio hilo tenia una llamada a Log("Conexion con servidor: "+ip+" Puerto: "+puerto); donde la funcion Log pintaba un textview y eso solo se puede hacer dentro de hilo principal con lo cual usando un handler se soluciona.

Una cuenstion mas.

En caso de querer usar para depurar la aplicación en un dispositivo real, habira que poner la ip de mi conexion a internet en vez del localhost? Un servidor apache?

Un saludo.

Arasthel
29/12/13, 17:18:56
Claro, tendrías que poner la IP del servidor en el que esté, si está en tu propio PC sería la IP externa de tu red. Si quieres que se quede fija, que probablemente será dinámica la que tengas contratada, puedes hacer uso de dyndns o algo parecido para tener una "url" que apunte a tu server.

Un saludo.

jtsamper
29/12/13, 17:32:17
Trabajo asíncrono con red -> hilo aparte. Ponlo en un Thread o en el doInBackground de un AsyncTask y no reventará.

@jtsamper: meter el código en un service sólo para eso es un poco matar moscas a cañonazos, a no ser que tenga que estar funcionando incluso si la app no o que tenga que compartirse en varias activities. Además, si no recuerdo mal, el código de un service sigue ejecutándose en el hilo principal (al menos si está en un proceso aparte).

Un saludo.
Cierto pero no se porque pensaba en un chat xD de hay que le dijera de meterlo en un service.

Arasthel
29/12/13, 17:35:17
Cierto pero no se porque pensaba en un chat xD de hay que le dijera de meterlo en un service.

Sí, la app parece ser para un chat por los nombres, así que igual el service si es una buena solución. Sólo quería aclarar que si era para una conexión sencilla no hace falta meter un service por medio :ok:

manolazo
02/01/14, 00:11:39
Hola, muchas gracias por la ayuda.

Hay una cosa que no me queda clara del codigo a ver si me la podeis explicar.


public class ChatServer {

public ChatServer (int port) throws IOException{
ServerSocket server = new ServerSocket(port);
while (true){
Socket client = server.accept();
System.out.println("Aceptado desde :"+client.getInetAddress());
ChatHandler c = new ChatHandler(client);
c.start();
}
}

public static void main (String args[]) throws IOException {
new ChatServer(30922);
}

}


Y es por parte del servidor ese bucle while:
No es un bucle en donde se repita la sentencia interna verdad? es decir , si pones un contador o un system.out.prinltn("lo que sea"); no se ejecuta indefinidamente, cuando en este tipo de bucles ha de repetirse indefinidamente hasta que no se cumpla la condicion.

Se supone que ese while es para quedarse indefinidamente a la escucha de aceptar conexiones pero no lo entiendo muy bien.

kriogeN
02/01/14, 08:41:55
"server.accept()" es bloqueante, es decir, se quedará ahí hasta que llegue una nueva solicitud de conexión por parte de un cliente.

Es algo muy común cuando se programa un servidor de sockets.

manolazo
02/01/14, 11:58:32
OK, no lo sabia.

Gracias.

Edito:

En este caso tambien es bloqueante la lectura de un dataInputStream para la lectura de datos verdad?

DataInputStream in = new DataInputStream(s.getInputStream());
while (true){
String sMensaje = in.readUTF();
ventana.mensajeRecibido(sMensaje);
}


Vale ya veo que si, ya esta claro. Un saludo