Кричим и слушаем одновременно
import android.content.Context; import android.net.DhcpInfo; import android.net.wifi.WifiManager; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; public class UDPHelper extends Thread { private BroadcastListener listener; private Context ctx; private DatagramSocket socket; private static final int PORT = 5050; public UDPHelper(Context ctx, BroadcastListener listener) throws IOException { this.listener = listener; this.ctx = ctx; } public void send(String msg) throws IOException { DatagramSocket clientSocket = new DatagramSocket(); clientSocket.setBroadcast(true); byte[] sendData = msg.getBytes(); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, getBroadcastAddress(), PORT); clientSocket.send(sendPacket); } @Override public void run() { try { socket = new DatagramSocket(PORT); } catch (SocketException e) { e.printStackTrace(); } while (!socket.isClosed()) { try { byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet); listener.onReceive( new String(packet.getData(), 0, packet.getLength()), packet.getAddress().getHostAddress()); } catch (IOException e) { e.printStackTrace(); } } socket.close(); } public void end() { socket.close(); } public interface BroadcastListener { public void onReceive(String msg, String ip); } InetAddress getBroadcastAddress() throws IOException { WifiManager wifi = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE); DhcpInfo dhcp = wifi.getDhcpInfo(); if(dhcp == null) return InetAddress.getByName("255.255.255.255"); int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask; byte[] quads = new byte[4]; for (int k = 0; k < 4; k++) quads[k] = (byte) ((broadcast >> k * 8) & 0xFF); return InetAddress.getByAddress(quads); } }
Этот класс - обычный поток, который открывает DatagramSocket и слушает его методом socket.receive(). При получении сообщения мы достаём текст и IP-адрес отправителя и передаём это методу onReceive() экземпляра класса BroadcastListener, который получили в конструкторе. Чтобы отправлять широковещательные сообщения используем метод send(). Заметьте, что сокет для отправки broadcast-ов мы открыли на том же порту, что и сокет для приёма сообщений. Никаких проблем с этим не возникает.
Как это использовать?
Очень просто:
List<String> ips = Collections.synchronizedList(new LinkedList<String>()); private class Pinger extends Thread { private boolean running; @Override public void run() { try { udp = new UDPHelper(getApplicationContext(), new UDPHelper.BroadcastListener() { @Override public void onReceive(String msg, String ip) { Log.v(TAG, "receive message "+msg+" from "+ip); if (!ips.contains(ip)) ips.add(ip); } }); udp.start(); } catch (IOException e) { e.printStackTrace(); } running = true; while (running) { try { udp.send("!PING!"); Log.v(TAG, "ping sended ...."); sleep(1000); } catch (Exception e) { e.printStackTrace(); } } } public void end() { running = false; udp.end(); } }
Делаем наш Pinger вложенным классом какого-нибудь Service, стартуем как обычный Thread и через очень короткое время имеем в переменной ips список IP-адресов всех кто шлёт сейчас запросы в нашей сети. Себя тоже мы там найдём, так что не забываем фильтровать свой IP-адрес. Скорость такого "поиска родственных душ" зависит только от задержки между пингами, которую (если не жалко трафика) можно сделать минимальной. Чтобы сохранять список IP-адресов актуальным, сохраняем время последнего пинга от данного клиента и вычищаем слишком "старые" адреса.
Nice Blog Post !
ОтветитьУдалитьЭтот комментарий был удален автором.
ОтветитьУдалитье
ОтветитьУдалитьА если не известен порт, как тогда?
ОтветитьУдалить