Problema con DatagramSocket en Android 7.1.1

Me enfrento a un tema muy extraño en QA's Google Pixel con Android 7.1.1 (N_MR1). Utilizamos UDP Server y Client para handshake durante el establecimiento de la conexión TCP.

QA informa de que el apretón de manos a Pixel no funciona. Después de explorar Logcat encontré que UdpServerTask lanza una excepción:

 java.net.BindException: Address already in use at java.net.PlainDatagramSocketImpl.bind0(Native Method) at java.net.AbstractPlainDatagramSocketImpl.bind(AbstractPlainDatagramSocketImpl.java:96) at java.net.DatagramSocket.bind(DatagramSocket.java:387) 

Lo que he intentado hasta ahora:

  • Habilitado Reuse address función de Reuse address (ver código) – sin suerte
  • Uso forzado de IPv4 (ver código) – mismo, no hay suerte
  • En el bucle, el rango de puertos comprobados (32100 – 32110) – tampoco ayuda. También todos los puertos lanzan la misma excepción java.net.BindException: Address already in use
  • Codificados en IP "0.0.0.0" y "10.1.xx" (ver código) – el mismo
  • Dispositivo reiniciado, red WiFi cambiada – no ayudó también

Además, he comprobado que utiliza los puertos en el dispositivo (NetStat + app) – IP y los puertos son libres, nadie se utiliza. Pero cuando traté de llamar bind() – la excepción sucede.

Al mismo tiempo UDP cliente (llamado a demanda) funciona bien – Puedo enviar paquetes UDP a través de puerto de destino.

También lo que noté – en mi Nexus con Android 7.1.1 y dispositivos con versión Android inferior no puedo reproducir el problema.

Ejemplo de prueba

 public class UDPServer { int PORT = 32100; long TIMEOUT = 30000; private void log(String msg) { System.out.println(msg); } private boolean isActive = false; public ArrayList<UdpServerTask> tasks = new ArrayList<>(); public void process(final byte[] data) { AsyncTask<Void, Void, Void> loadTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { //process data return null; } }; Utils.executeTask(loadTask); } public void startAddress(String host) { UdpServerTask loadTask = new UdpServerTask(host, PORT); tasks.add(loadTask); Utils.executeTask(loadTask); } public void runUdpServer() { java.lang.System.setProperty("java.net.preferIPv6Addresses", "false"); java.lang.System.setProperty("java.net.preferIPv4Stack", "true"); stop_UDP_Server(); isActive = true; AsyncTask<Void, Void, Void> mainTask = new AsyncTask<Void, Void, Void>() { ArrayList<String> ips = new ArrayList<>(); @Override protected Void doInBackground(Void... params) { log("UDP starting servers "); ips.add(null); ips.add("0.0.0.0"); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface networkInterface = interfaces.nextElement(); if (networkInterface.isLoopback() || !networkInterface.isUp()) { continue; } for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { InetAddress broadcast = interfaceAddress .getBroadcast(); if (broadcast == null || broadcast instanceof Inet6Address) { continue; } if (!ips.contains(broadcast.getHostAddress())) { ips.add(broadcast.getHostAddress()); } } } } catch (final Throwable e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { for (String host : ips) { startAddress(host); } } }; Utils.executeTask(mainTask); } public boolean reallyStopped() { return !isActive && tasks.isEmpty(); } public void stop_UDP_Server() { isActive = false; AsyncTask<Void, Void, Void> mainTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { log("UDP start stopping"); for (UdpServerTask task : tasks) { task.cancelServer(); } tasks.clear(); return null; } }; Utils.executeTask(mainTask); while (!reallyStopped()) { try { Thread.sleep(100); } catch (Exception e) { } } } private class UdpServerTask extends AsyncTask<Void, Void, Void> { String ip; int port; public UdpServerTask(String ip, int port) { this.ip = ip; this.port = port; } DatagramSocket ds = null; public void cancelServer() { log("UDP server cancelServer"); if (ds != null && !ds.isClosed()) { try { ds.close(); ds = null; } catch (Exception e) { e.printStackTrace(); } } log("UDP server stopped"); } @Override protected Void doInBackground(Void... params) { long time = System.currentTimeMillis(); boolean firstAttempt = true; while (System.currentTimeMillis() - time <= TIMEOUT && isActive) { try { if (ds != null && !ds.isClosed()) { try { ds.close(); ds = null; } catch (Exception e) { e.printStackTrace(); } } log("UDP try create connection " + this.ip + ":" + this.port); if (firstAttempt) { ds = new DatagramSocket(new InetSocketAddress(TextUtils.isEmpty(this.ip) ? null : InetAddress.getByName(this.ip), this.port)); } else { ds = new DatagramSocket(null); } ds.setBroadcast(true); if (!firstAttempt) { ds.setReuseAddress(true); ds.bind(new InetSocketAddress(TextUtils.isEmpty(this.ip) ? null : InetAddress.getByName(this.ip), this.port)); } long start = System.currentTimeMillis(); while (!ds.isBound()) { if (System.currentTimeMillis() - start >= TIMEOUT) { throw new Exception("Cann't bind to " + this.ip + ":" + this.port); } Thread.sleep(150); } log("UDP Server Started on " + this.ip + ":" + this.port); while (isActive) { final byte[] lMsg = new byte[4096]; final DatagramPacket dp = new DatagramPacket(lMsg, lMsg.length); ds.receive(dp); log("process UDP from " + dp.getAddress().toString() + ":" + dp.getPort()); process(dp.getData()); } log("UDP Server Stopped on " + this.ip + ":" + this.port); } catch (final Throwable e) { e.printStackTrace(); firstAttempt = false; log("UDP Server Failed " + this.ip + ":" + this.port + " " + e); try { Thread.sleep(TIMEOUT / 10); } catch (Exception ex) { } } } if (ds != null && !ds.isClosed()) try { ds.close(); ds = null; } catch (Exception e) { e.printStackTrace(); } log("UDP Server finish task"); return null; } } } 

One Solution collect form web for “Problema con DatagramSocket en Android 7.1.1”

El problema está en el puerto que usa. En mi teléfono Pixel, los siguientes rangos de puertos se definen en el archivo /proc/sys/net/ipv4/ip_local_reserved_ports :

32100-32600,40100-40150

Si cambio el número de puerto en su código a algo fuera de este rango (y por encima de 1024, por supuesto), funciona bien y puedo enviar datos a la aplicación desde el otro host.

La documentación de Linux Kernel describe este archivo como este:

ip_local_reserved_ports – lista de rangos separados por comas

Especifique los puertos que están reservados para aplicaciones de terceros conocidas. Estos puertos no se utilizarán mediante asignaciones automáticas de puertos (por ejemplo, al llamar a connect() o bind() con el número de puerto 0). El comportamiento explícito de asignación de puertos no ha cambiado .

Por lo tanto, cuando explícitamente pasar el número de puerto para el método de bind todavía debe ser posible utilizar esos puertos. Aparentemente esto no funciona. En mi opinión, hay un error en algún lugar de la pila de red proporcionado por la implementación de Kernel de Linux utilizada en Android. Pero esto requiere investigación adicional.

También puede resultar útil la siguiente lista de contenidos de ip_local_reserved_ports en diferentes teléfonos: https://census.tsyrklevich.net/sysctls/net.ipv4.ip_local_reserved_ports

  • Solución de streaming UDP o RTP para android
  • Unicast UDP de Android funciona, pero la difusión no
  • Envío de paquetes UDP desde ANDROID 2.2 (deseo de HTC)
  • ¿Cómo puedo convertir ByteArrayInputStream a jpeg y mostrar en android móvil?
  • Paquetes UDP (vía WiFi Direct) nunca llegan
  • ¿Tiene sentido tener más de un socket UDP Datagram en espera? ¿Los paquetes "simultáneos" han sido eliminados o puestos en cola por el núcleo?
  • Mantenimiento de una conexión bidireccional UDP
  • ¿Problemas con el uso de TCP y UDP en la misma aplicación?
  • UDP Hole Punching no es posible con el proveedor móvil
  • Emulador de Android udp broadcast
  • ¿Fuerza el androide para enviar paquetes UDP inmediatamente?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.