Простое сетевое server/client приложение

от
Android    network

Реализацию можно разделить на две части:

1. поиск и регистрацию сервиса (сервера) в сети
2. создание сервера и клиента

Первый пункт начиная с Android 4.1 можно реализовать средствами стандартного API - http://developer.android.com/t...-wirelessly/nsd.html
Но я буду использовать реализацию mDNS (multicast DNS) на Java - JmDNS. Jar файл содержит дублированные файлы, поэтому нужно оставить только по одному, иначе Eclipse будет выдавать странные ошибки при сборке или запуске apk. Вот уже нормальный файл, заодно удалены ненужные классы jmdns.jar .
Примерно такой же принцип регистрации сервиса используют принтеры и другое оборудование.

Второй пункт реализуется через стандартные Java API - ServerSocket & Socket.

Ниже приведен код приложения с комментариями. Создается сервер, запускается сервис в локальной сети. При обнаружении другого сервиса с подходящими параметрами (тип, протокол, имя и т.д.) подключаемся к нему и шлем тестовые данные, сервер на другом устройстве их принимает и останавливает сервер.

  1. package com.android.testjmdns;
  2.  
  3. import java.io.DataInputStream;
  4. import java.io.DataOutputStream;
  5. import java.io.IOException;
  6. import java.net.InetAddress;
  7. import java.net.InetSocketAddress;
  8. import java.net.ServerSocket;
  9. import java.net.Socket;
  10.  
  11. import javax.jmdns.JmDNS;
  12. import javax.jmdns.ServiceEvent;
  13. import javax.jmdns.ServiceInfo;
  14. import javax.jmdns.ServiceListener;
  15.  
  16. import android.app.Activity;
  17. import android.content.Context;
  18. import android.net.wifi.WifiManager;
  19. import android.os.Bundle;
  20. import android.widget.TextView;
  21. import android.os.Handler;
  22.  
  23. public class TestJmDNS extends Activity {
  24.  
  25.     WifiManager.MulticastLock lock;
  26.     Handler handler = new Handler();
  27.     TextView log;
  28.  
  29.     static final int PORT = 5431;
  30.     static final String TYPE = "_myprotocol._tcp.local.";
  31.     Thread srvThread;
  32.     ServerSocket server;
  33.  
  34.     /**
  35.      * Called when the activity is first created.
  36.      */
  37.     @Override
  38.     public void onCreate(Bundle savedInstanceState) {
  39.         super.onCreate(savedInstanceState);
  40.         setContentView(R.layout.main);
  41.         log = (TextView) findViewById(R.id.text);
  42.  
  43.         srvThread = new Thread(new Runnable() {
  44.             public void run() {
  45.                 try {
  46.                     server = new ServerSocket();
  47.                     server.bind(new InetSocketAddress(getIpAddr(), PORT));
  48.                     notifyUser("Server started");
  49.                     Socket client = server.accept(); // Останавливает поток до подключения клиента
  50.                     if (!Thread.interrupted() && client != null) { // Если поток не остановлен и все Ок, то читаем данные от клиента
  51.                         notifyUser("Client connected to my server");
  52.                         DataInputStream dis = new DataInputStream(client.getInputStream());
  53.                         String msg = dis.readUTF();
  54.                         notifyUser("Receive message from client: " + msg);
  55.                         client.close();
  56.                     }
  57.                 } catch (IOException e) {
  58.                     e.printStackTrace();
  59.                 } finally {
  60.                     if (!server.isClosed()) {
  61.                         try {
  62.                             server.close();
  63.                         } catch (IOException e) {
  64.                             // TODO Auto-generated catch block
  65.                             e.printStackTrace();
  66.                         }
  67.                     }
  68.                 }
  69.             }
  70.         });
  71.         srvThread.start();
  72.         /* В фоновом потоке, потому что работа с сетью не должна проводиться в UI-thread */
  73.         new Thread(new Runnable() {
  74.             public void run() {
  75.                 try {
  76.                     /* Ждем пока стартует сервер, если планируется взаимодействие с ним
  77.                      * на этом же устройстве, в данном случае не обязательно */
  78.                     srvThread.join(1000);
  79.                 } catch (InterruptedException e) {
  80.                     e.printStackTrace();
  81.                 }
  82.                 setUp();
  83.             }
  84.         }).start();
  85.  
  86.     }
  87.  
  88.     private JmDNS jmdns;
  89.     private ServiceListener listener;
  90.     private ServiceInfo serviceInfo;
  91.  
  92.     private void setUp() {
  93.         final WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
  94.         /* Следующий код включает поддержку multicast в приложении, она отключена по умолчанию.
  95.          * Без него JmDNS не будет работать */
  96.         lock = wifi.createMulticastLock("mylock");
  97.         lock.setReferenceCounted(true);
  98.         lock.acquire();
  99.         try {
  100.             /* Создаем JmDNS и слушатель событий для него */
  101.             InetAddress deviceIpAddress = InetAddress.getByName(getIpAddr(wifi));
  102.             jmdns = JmDNS.create(deviceIpAddress, "MyAndroidHostName");
  103.             jmdns.addServiceListener(TYPE, listener = new ServiceListener() {
  104.  
  105.                 @Override
  106.                 public void serviceResolved(ServiceEvent ev) {
  107.                     notifyUser("Service resolved: " + ev.getInfo().getQualifiedName() + " port:" + ev.getInfo().getPort());
  108.                     /* Если сервис запущен на этом же устройстве, то пропускаем его */
  109.                     if (getIpAddr(wifi).equals(ev.getInfo().getInetAddresses()[0].getCanonicalHostName())) {
  110.                         return;
  111.                     }
  112.                     Socket client = null;
  113.                     try {
  114.                         /* Обнаружен сервер, шлем ему данные с помощью Socket */
  115.                         String url = ev.getInfo().getInetAddresses()[0].getCanonicalHostName() + ":" + ev.getInfo().getPort();
  116.                         notifyUser("Create client to " + url);
  117.                         client = new Socket(ev.getInfo().getInetAddresses()[0], ev.getInfo().getPort());
  118.                         notifyUser("Client created to " + url);
  119.                         DataOutputStream dos = new DataOutputStream(client.getOutputStream());
  120.                         dos.writeUTF("Hello from " + android.os.Build.MODEL + "!!!");
  121.                         notifyUser("Client sends message to " + url);
  122.                         dos.flush();
  123.                         dos.close();
  124.                     } catch (IOException e) {
  125.                         // TODO Auto-generated catch block
  126.                         e.printStackTrace();
  127.                     } finally {
  128.                         if (client != null && !client.isClosed()) {
  129.                             try {
  130.                                 client.close();
  131.                             } catch (IOException e) {
  132.                                 // TODO Auto-generated catch block
  133.                                 e.printStackTrace();
  134.                             }
  135.                         }
  136.                     }
  137.                 }
  138.  
  139.                 @Override
  140.                 public void serviceRemoved(ServiceEvent ev) {
  141.                     notifyUser("Service removed: " + ev.getName());
  142.                 }
  143.  
  144.                 @Override
  145.                 public void serviceAdded(ServiceEvent event) {
  146.                     // Для вызова serviceResolved(...)
  147.                     jmdns.requestServiceInfo(event.getType(), event.getName(), 1 /* timeout 1ms*/);
  148.                 }
  149.             });
  150.             /* Создаем наш сервис, теперь другие устройства его смогут найти по протоколу multicast DNS (mDNS) */
  151.             serviceInfo = ServiceInfo.create(TYPE, "AndroidTest", PORT, "My android server");
  152.             jmdns.registerService(serviceInfo);
  153.         } catch (IOException e) {
  154.             e.printStackTrace();
  155.         }
  156.     }
  157.  
  158.     private void notifyUser(final String msg) {
  159.         handler.postDelayed(new Runnable() {
  160.             public void run() {
  161.                 log.setText(log.getText() + "\n" + msg);
  162.             }
  163.         }, 1);
  164.  
  165.     }
  166.  
  167.     @Override
  168.     protected void onStop() {
  169.         /* Когда activity останавливается, все закрываем */
  170.         if (jmdns != null) {
  171.             if (listener != null) {
  172.                 jmdns.removeServiceListener(TYPE, listener);
  173.                 listener = null;
  174.             }
  175.             jmdns.unregisterAllServices();
  176.             try {
  177.                 jmdns.close();
  178.             } catch (IOException e) {
  179.                 // TODO Auto-generated catch block
  180.                 e.printStackTrace();
  181.             }
  182.             jmdns = null;
  183.         }
  184.         srvThread.interrupt();
  185.         if (server != null && !server.isClosed()) {
  186.             try {
  187.                 /* Если сервер все еще ожидает клиента (висит на методе accept()
  188.                  * то метод close() прервет его вызвав исключение */
  189.                 server.close();
  190.             } catch (IOException e) {
  191.                 // TODO Auto-generated catch block
  192.                 e.printStackTrace();
  193.             }
  194.         }
  195.         if (lock != null) {
  196.             lock.release();
  197.             lock = null;
  198.         }
  199.         super.onStop();
  200.     }
  201.  
  202.     public static String getIpAddr(WifiManager wifiManager) {
  203.         int ip = wifiManager.getConnectionInfo().getIpAddress();
  204.         return String.format(
  205.                 "%d.%d.%d.%d",
  206.                 (ip & 0xff),
  207.                 (ip >> 8 & 0xff),
  208.                 (ip >> 16 & 0xff),
  209.                 (ip >> 24 & 0xff));
  210.     }
  211.  
  212.     public String getIpAddr() {
  213.         return getIpAddr((WifiManager) getSystemService(Context.WIFI_SERVICE));
  214.     }
  215. }

Результат работы программы можно увидеть на снимках экрана:
Устройство отсылает данные
  scr1.png
Другое принимает
  scr2.png

Все исходники здесь
За основу брался этот проект
  • +2
  • views 8307