Omówienie Wi-Fi Direct (peer-to-peer lub P2P)

Wi-Fi Direct (P2P) umożliwia urządzeniom z odpowiednim sprzętem łączenie się bezpośrednio ze sobą przez Wi-Fi bez pośredniego punktu dostępu. Te interfejsy API pozwalają na wykrywanie innych urządzeń i łączenie się z nimi, jeśli każde z nich obsługuje sieć Wi-Fi P2P, a następnie komunikowanie się przez szybkie łącze na odległość dużo większą niż w przypadku połączenia Bluetooth. Przydaje się to w aplikacjach, które udostępniają dane między użytkownikami, np. w grach wieloosobowych czy aplikacjach do udostępniania zdjęć.

Interfejsy Wi-Fi API P2P składają się z tych głównych elementów:

  • Metody, które umożliwiają wykrywanie elementów równorzędnych, wysyłanie do nich żądań i łączenie się z nimi, które są zdefiniowane w klasie WifiP2pManager.
  • Detektory, które umożliwiają otrzymywanie powiadomień o udanej lub nieudanej próbie wywołania metody WifiP2pManager. Przy wywoływaniu metod WifiP2pManager każda metoda może otrzymać określony odbiornik przekazany jako parametr.
  • Intencje powiadamiające o konkretnych zdarzeniach wykrytych przez platformę P2P sieci Wi-Fi, takich jak utrata połączenia lub nowo wykryte połączenie równorzędne.

Te 3 główne komponenty interfejsów API często są używane razem. Możesz na przykład podać WifiP2pManager.ActionListener wywołania discoverPeers(), aby metody ActionListener.onSuccess() i ActionListener.onFailure() mogły Cię powiadomić. Zamiar WIFI_P2P_PEERS_CHANGED_ACTION jest też przesyłany, gdy metoda discoverPeers() wykryje, że lista podobnych reklamodawców się zmieniła.

Omówienie interfejsu API

Klasa WifiP2pManager udostępnia metody, które umożliwiają interakcję ze sprzętem Wi-Fi Twojego urządzenia i na przykład wykrywanie sieci równorzędnych i łączenie się z nimi. Dostępne są te opcje:

Tabela 1. Metody P2P sieci Wi-Fi

Metoda Description
initialize() Rejestruje aplikację za pomocą platformy Wi-Fi. Wywołaj to przed wywołaniem dowolnej innej metody P2P sieci Wi-Fi.
connect() Rozpoczyna połączenie peer-to-peer z urządzeniem o określonej konfiguracji.
cancelConnect() Anuluje trwające negocjacje w grupie peer-to-peer.
requestConnectInfo() Żąda informacji o połączeniu z urządzeniem.
createGroup() Tworzy grupę peer-to-peer z obecnym urządzeniem jako właścicielem grupy.
removeGroup() Usuwa bieżącą grupę peer-to-peer.
requestGroupInfo() Wysyła żądanie informacji o grupie peer-to-peer.
discoverPeers() Inicjuje badanie porównawcze.
requestPeers() Wysyła żądanie udostępnienia bieżącej listy wykrytych elementów równorzędnych.

Metody WifiP2pManager umożliwiają przekazywanie przez odbiornik, dzięki czemu platforma P2P sieci Wi-Fi może powiadamiać Twoją aktywność o stanie połączenia. Dostępne interfejsy nasłuchiwania i odpowiadające im wywołania metody WifiP2pManager, które korzystają z detektorów, zostały opisane w tabeli 2.

Tabela 2. Detektory P2P sieci Wi-Fi

Interfejs detektora Powiązane działania
WifiP2pManager.ActionListener connect(), cancelConnect(), createGroup(), removeGroup() i discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()

Interfejsy Wi-Fi P2P API definiują intencje, które są transmitowane, gdy występują określone zdarzenia P2P sieci Wi-Fi, np. gdy zostaje wykryte nowe połączenie równorzędne lub zmiana stanu sieci Wi-Fi urządzenia. Możesz zarejestrować się, aby otrzymywać te intencje w swojej aplikacji, tworząc odbiornik, który obsługuje te intencje:

Tabela 3. Intencje P2P sieci Wi-Fi

Zamiar Description
WIFI_P2P_CONNECTION_CHANGED_ACTION Transmituj w przypadku zmiany stanu połączenia Wi-Fi na urządzeniu.
WIFI_P2P_PEERS_CHANGED_ACTION Przesyłaj, gdy dzwonisz do: discoverPeers(). Jeśli obsługujesz tę intencję w swojej aplikacji, zwykle wywołujesz requestPeers(), aby uzyskać zaktualizowaną listę połączeń równorzędnych.
WIFI_P2P_STATE_CHANGED_ACTION Przesyłanie, gdy sieć Wi-Fi P2P jest włączona lub wyłączona na urządzeniu.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION Komunikaty po zmianie informacji o urządzeniu, np. jego nazwy.

Tworzenie odbiornika dla intencji P2P sieci Wi-Fi

Odbiornik umożliwia odbieranie intencji przesyłanych przez system Android, dzięki czemu aplikacja może reagować na zdarzenia, które Cię interesują. Oto podstawowe czynności, które należy wykonać, aby utworzyć odbiornik transmisji do obsługi intencji P2P sieci Wi-Fi:

  1. Utwórz klasę, która stanowi rozszerzenie klasy BroadcastReceiver. W konstruktorze klasy użyjesz parametrów WifiP2pManager i WifiP2pManager.Channel oraz działania, w którym zostanie zarejestrowany odbiornik. Dzięki temu odbiornik może wysyłać aktualizacje aktywności oraz w razie potrzeby mieć dostęp do sprzętu Wi-Fi i kanału komunikacyjnego.

  2. W odbiorniku sprawdź intencje, które Cię interesują w metodzie onReceive(). Wykonaj odpowiednie działania w zależności od otrzymanej intencji. Jeśli na przykład odbiornik otrzymuje intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz wywołać metodę requestPeers(), aby uzyskać listę aktualnie wykrytych elementów równorzędnych.

Poniższy kod pokazuje, jak utworzyć typowego odbiornika. Odbiornik wykorzystuje WifiP2pManager obiekt i działanie jako argumenty, a następnie używa tych 2 klas do odpowiedniego wykonania wymaganych działań, gdy odbiornik otrzyma intencję:

Kotlin

/**
* A BroadcastReceiver that notifies of important Wi-Fi p2p events.
*/
class WiFiDirectBroadcastReceiver(
       private val manager: WifiP2pManager,
       private val channel: WifiP2pManager.Channel,
       private val activity: MyWifiActivity
) : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       val action: String = intent.action
       when (action) {
           WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
               // Check to see if Wi-Fi is enabled and notify appropriate activity
           }
           WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
               // Call WifiP2pManager.requestPeers() to get a list of current peers
           }
           WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
               // Respond to new connection or disconnections
           }
           WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
               // Respond to this device's wifi state changing
           }
       }
   }
}

Java

/**
* A BroadcastReceiver that notifies of important Wi-Fi p2p events.
*/
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

   private WifiP2pManager manager;
   private Channel channel;
   private MyWiFiActivity activity;

   public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
           MyWifiActivity activity) {
       super();
       this.manager = manager;
       this.channel = channel;
       this.activity = activity;
   }

   @Override
   public void onReceive(Context context, Intent intent) {
       String action = intent.getAction();

       if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
           // Check to see if Wi-Fi is enabled and notify appropriate activity
       } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
           // Call WifiP2pManager.requestPeers() to get a list of current peers
       } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
           // Respond to new connection or disconnections
       } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
           // Respond to this device's wifi state changing
       }
   }
}

Na urządzeniach z Androidem 10 lub nowszym te intencje przesyłania nie są przyklejone:

WIFI_P2P_CONNECTION_CHANGED_ACTION
Do pobierania bieżących informacji o połączeniu aplikacje mogą używać poleceń requestConnectionInfo(), requestNetworkInfo() lub requestGroupInfo().
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Aplikacje mogą pobierać bieżące informacje o połączeniu za pomocą requestDeviceInfo().

Utwórz aplikację P2P sieci Wi-Fi

Utworzenie aplikacji P2P sieci Wi-Fi obejmuje utworzenie i zarejestrowanie odbiornika transmisji dla aplikacji, odnajdywanie sieci równorzędnych, nawiązanie połączenia z peerem i przesłanie danych do niego. W sekcjach poniżej opisujemy, jak to zrobić.

Konfiguracja początkowa

Przed skorzystaniem z interfejsów Wi-Fi API P2P upewnij się, że aplikacja ma dostęp do sprzętu, a urządzenie obsługuje protokół P2P sieci Wi-Fi. Jeśli sieć Wi-Fi P2P jest obsługiwana, możesz uzyskać instancję WifiP2pManager, utworzyć i zarejestrować odbiornik, a potem zacząć korzystać z interfejsów Wi-Fi P2P API.

  1. Poproś o pozwolenie na korzystanie ze sprzętu Wi-Fi na urządzeniu i zadeklaruj, że Twoja aplikacja ma w pliku manifestu Androida prawidłową minimalną wersję pakietu SDK:

    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- If your app targets Android 13 (API level 33)
         or higher, you must declare the NEARBY_WIFI_DEVICES permission. -->
    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
                     <!-- If your app derives location information from
                          Wi-Fi APIs, don't include the "usesPermissionFlags"
                          attribute. -->
                     android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
                     <!-- If any feature in your app relies on precise location
                          information, don't include the "maxSdkVersion"
                          attribute. -->
                     android:maxSdkVersion="32" />
    

    Oprócz poprzednich uprawnień następujące interfejsy API wymagają również włączenia trybu lokalizacji:

  2. Sprawdź, czy sieć Wi-Fi P2P jest włączona i obsługiwana. Dobrze jest to sprawdzić w odbiorniku, gdy otrzymuje intencję WIFI_P2P_STATE_CHANGED_ACTION. Powiadamiaj o stanie połączenia Wi-Fi o stanie P2P i odpowiednio zareaguj:

    Kotlin

    override fun onReceive(context: Context, intent: Intent) {
    ...
    val action: String = intent.action
    when (action) {
       WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
           val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
           when (state) {
               WifiP2pManager.WIFI_P2P_STATE_ENABLED -> {
                   // Wifi P2P is enabled
               }
               else -> {
                   // Wi-Fi P2P is not enabled
               }
           }
       }
    }
    ...
    }
    

    Java

    @Override
    public void onReceive(Context context, Intent intent) {
    ...
    String action = intent.getAction();
    if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
       int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
       if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
           // Wifi P2P is enabled
       } else {
           // Wi-Fi P2P is not enabled
       }
    }
    ...
    }
    
  3. W metodzie onCreate() aktywności uzyskaj wystąpienie WifiP2pManager i zarejestruj aplikację w platformie P2P Wi-Fi, wywołując initialize(). Ta metoda zwraca kod WifiP2pManager.Channel, który służy do połączenia aplikacji z platformą P2P sieci Wi-Fi. Utwórz też instancję odbiornika z obiektami WifiP2pManager i WifiP2pManager.Channel wraz z odniesieniem do swojej aktywności. Dzięki temu odbiornik będzie mógł powiadomić Cię o interesujących wydarzeniach i odpowiednio je zaktualizować. W razie potrzeby możesz też zmieniać stan Wi-Fi urządzenia:

    Kotlin

    val manager: WifiP2pManager? by lazy(LazyThreadSafetyMode.NONE) {
       getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
    }
    
    var channel: WifiP2pManager.Channel? = null
    var receiver: BroadcastReceiver? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
       ...
    
       channel = manager?.initialize(this, mainLooper, null)
       channel?.also { channel ->
           receiver = WiFiDirectBroadcastReceiver(manager, channel, this)
       }
    }
    

    Java

    WifiP2pManager manager;
    Channel channel;
    BroadcastReceiver receiver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState){
       ...
       manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
       channel = manager.initialize(this, getMainLooper(), null);
       receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
       ...
    }
    
  4. Utwórz filtr intencji i dodaj te same intencje, które sprawdza odbiornik:

    Kotlin

    val intentFilter = IntentFilter().apply {
       addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)
       addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
       addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
       addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
    }
    

    Java

    IntentFilter intentFilter;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState){
       ...
       intentFilter = new IntentFilter();
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
       ...
    }
    
  5. Zarejestruj odbiornik, korzystając z metody onResume() swojej aktywności, i wyrejestruj go, korzystając z metody onPause():

    Kotlin

    /* register the broadcast receiver with the intent values to be matched */
    override fun onResume() {
       super.onResume()
       receiver?.also { receiver ->
           registerReceiver(receiver, intentFilter)
       }
    }
    
    /* unregister the broadcast receiver */
    override fun onPause() {
       super.onPause()
       receiver?.also { receiver ->
           unregisterReceiver(receiver)
       }
    }
    

    Java

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
       super.onResume();
       registerReceiver(receiver, intentFilter);
    }
    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
       super.onPause();
       unregisterReceiver(receiver);
    }
    
  6. Gdy masz urządzenie WifiP2pManager.Channel i skonfigurujesz odbiornik, aplikacja może wykonywać połączenia P2P przez Wi-Fi i odbierać intencje P2P Wi-Fi.

  7. Zaimplementuj swoją aplikację, korzystając z funkcji P2P sieci Wi-Fi, wywołując te metody w komponencie WifiP2pManager.

W następnych sekcjach opisujemy, jak wykonywać typowe działania, takie jak odkrywanie aplikacji z grupy porównawczej i nawiązywanie z nimi więzi.

Odkryj aplikacje z grupy porównawczej

Wywołaj discoverPeers(), aby wykryć dostępnych peerów, którzy są w zasięgu i mogą się z nim połączyć. Wywołanie tej funkcji jest asynchroniczne, a o powodzenie lub niepowodzenie są przekazywane do aplikacji za pomocą funkcji onSuccess() i onFailure(), jeśli utworzono WifiP2pManager.ActionListener. Metoda onSuccess() informuje tylko o pomyślnym procesie wykrywania i nie dostarcza żadnych informacji o elementach równorzędnych, które wykryła. Poniższy przykładowy kod pokazuje, jak to skonfigurować.

Kotlin

manager?.discoverPeers(channel, object : WifiP2pManager.ActionListener {

   override fun onSuccess() {
       ...
   }

   override fun onFailure(reasonCode: Int) {
       ...
   }
})

Java

manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
   @Override
   public void onSuccess() {
       ...
   }

   @Override
   public void onFailure(int reasonCode) {
       ...
   }
});

Jeśli proces wykrywania się powiedzie i wykryje peera, system przekaże intencję WIFI_P2P_PEERS_CHANGED_ACTION, której można nasłuchiwać w odbiorniku, aby uzyskać listę innych aplikacji. Gdy aplikacja otrzyma intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz poprosić o listę wykrytych elementów równorzędnych za pomocą requestPeers(). Poniższy kod pokazuje, jak to zrobić.

Kotlin

override fun onReceive(context: Context, intent: Intent) {
   val action: String = intent.action
   when (action) {
       ...
       WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
           manager?.requestPeers(channel) { peers: WifiP2pDeviceList? ->
               // Handle peers list
           }
       }
       ...
   }
}

Java

PeerListListener myPeerListListener;
...
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

   // request available peers from the wifi p2p manager. This is an
   // asynchronous call and the calling activity is notified with a
   // callback on PeerListListener.onPeersAvailable()
   if (manager != null) {
       manager.requestPeers(channel, myPeerListListener);
   }
}

Metoda requestPeers() również jest asynchroniczna i może powiadamiać działanie użytkownika o dostępności listy połączeń równorzędnych w onPeersAvailable(), która jest definiowana w interfejsie WifiP2pManager.PeerListListener. Metoda onPeersAvailable() udostępnia WifiP2pDeviceList, który można wielokrotnie przeglądać, aby znaleźć peera, z którym chcesz się połączyć.

Połącz się z grupą porównawczą

Po uzyskaniu listy możliwych peerów i wybraniu urządzenia, z którym chcesz się połączyć, wywołaj metodę connect(), aby połączyć się z urządzeniem. To wywołanie metody wymaga obiektu WifiP2pConfig zawierającego informacje o urządzeniu, z którym ma zostać nawiązane połączenie. WifiP2pManager.ActionListener może powiadomić Cię o udanym lub nieudanym nawiązaniu połączenia. Poniższy kod pokazuje, jak utworzyć połączenie z urządzeniem.

Kotlin

val device: WifiP2pDevice = ...
val config = WifiP2pConfig()
config.deviceAddress = device.deviceAddress
channel?.also { channel ->
   manager?.connect(channel, config, object : WifiP2pManager.ActionListener {

       override fun onSuccess() {
           //success logic
       }

       override fun onFailure(reason: Int) {
           //failure logic
       }
   }
)}

Java

//obtain a peer from the WifiP2pDeviceList
WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
manager.connect(channel, config, new ActionListener() {

   @Override
   public void onSuccess() {
       //success logic
   }

   @Override
   public void onFailure(int reason) {
       //failure logic
   }
});

Przenoszenie danych

Po nawiązaniu połączenia będzie można przesyłać dane między urządzeniami z gniazdkami. Podstawowe etapy przenoszenia danych:

  1. Utwórz ServerSocket. To gniazdo oczekuje na połączenie od klienta przez określony port i blokuje się, dopóki nie nastąpi. Zrób to w wątku w tle.
  2. Utwórz klienta Socket. Klient łączy się z urządzeniem serwera za pomocą adresu IP i portu gniazda serwera.
  3. Wysyłanie danych klienta na serwer. Gdy gniazdo klienta połączy się z gniazdem serwera, będzie można wysyłać dane od klienta do serwera za pomocą strumieni bajtów.
  4. Gniazdo serwera oczekuje na połączenie z klientem (w metodzie accept()). To wywołanie jest blokowane do czasu, gdy klient się połączy, więc wywołaj je w innym wątku. Po nawiązaniu połączenia serwer może odebrać dane od klienta.

Poniższy przykład, zmodyfikowany na podstawie demonstracji P2P sieci Wi-Fi, pokazuje, jak utworzyć komunikację typu klient-serwer i jak przesłać obrazy JPEG z klienta na serwer z usługą. Aby uzyskać pełny przykład działania, skompiluj i uruchom wersję demonstracyjną.

Kotlin

class FileServerAsyncTask(
       private val context: Context,
       private var statusText: TextView
) : AsyncTask<Void, Void, String?>() {

   override fun doInBackground(vararg params: Void): String? {
       /**
        * Create a server socket.
        */
       val serverSocket = ServerSocket(8888)
       return serverSocket.use {
           /**
            * Wait for client connections. This call blocks until a
            * connection is accepted from a client.
            */
           val client = serverSocket.accept()
           /**
            * If this code is reached, a client has connected and transferred data
            * Save the input stream from the client as a JPEG file
            */
           val f = File(Environment.getExternalStorageDirectory().absolutePath +
                   "/${context.packageName}/wifip2pshared-${System.currentTimeMillis()}.jpg")
           val dirs = File(f.parent)

           dirs.takeIf { it.doesNotExist() }?.apply {
               mkdirs()
           }
           f.createNewFile()
           val inputstream = client.getInputStream()
           copyFile(inputstream, FileOutputStream(f))
           serverSocket.close()
           f.absolutePath
       }
   }

   private fun File.doesNotExist(): Boolean = !exists()

   /**
    * Start activity that can handle the JPEG image
    */
   override fun onPostExecute(result: String?) {
       result?.run {
           statusText.text = "File copied - $result"
           val intent = Intent(android.content.Intent.ACTION_VIEW).apply {
               setDataAndType(Uri.parse("file://$result"), "image/*")
           }
           context.startActivity(intent)
       }
   }
}

Java

public static class FileServerAsyncTask extends AsyncTask {

   private Context context;
   private TextView statusText;

   public FileServerAsyncTask(Context context, View statusText) {
       this.context = context;
       this.statusText = (TextView) statusText;
   }

   @Override
   protected String doInBackground(Void... params) {
       try {

           /**
            * Create a server socket and wait for client connections. This
            * call blocks until a connection is accepted from a client
            */
           ServerSocket serverSocket = new ServerSocket(8888);
           Socket client = serverSocket.accept();

           /**
            * If this code is reached, a client has connected and transferred data
            * Save the input stream from the client as a JPEG file
            */
           final File f = new File(Environment.getExternalStorageDirectory() + "/"
                   + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()
                   + ".jpg");

           File dirs = new File(f.getParent());
           if (!dirs.exists())
               dirs.mkdirs();
           f.createNewFile();
           InputStream inputstream = client.getInputStream();
           copyFile(inputstream, new FileOutputStream(f));
           serverSocket.close();
           return f.getAbsolutePath();
       } catch (IOException e) {
           Log.e(WiFiDirectActivity.TAG, e.getMessage());
           return null;
       }
   }

   /**
    * Start activity that can handle the JPEG image
    */
   @Override
   protected void onPostExecute(String result) {
       if (result != null) {
           statusText.setText("File copied - " + result);
           Intent intent = new Intent();
           intent.setAction(android.content.Intent.ACTION_VIEW);
           intent.setDataAndType(Uri.parse("file://" + result), "image/*");
           context.startActivity(intent);
       }
   }
}

Po stronie klienta połącz się z gniazdem serwera przez gniazdo klienta i przenieś dane. W tym przykładzie przesyła się plik JPEG w systemie plików urządzenia klienckiego.

Kotlin

val context = applicationContext
val host: String
val port: Int
val len: Int
val socket = Socket()
val buf = ByteArray(1024)
...
try {
   /**
    * Create a client socket with the host,
    * port, and timeout information.
    */
   socket.bind(null)
   socket.connect((InetSocketAddress(host, port)), 500)

   /**
    * Create a byte stream from a JPEG file and pipe it to the output stream
    * of the socket. This data is retrieved by the server device.
    */
   val outputStream = socket.getOutputStream()
   val cr = context.contentResolver
   val inputStream: InputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg"))
   while (inputStream.read(buf).also { len = it } != -1) {
       outputStream.write(buf, 0, len)
   }
   outputStream.close()
   inputStream.close()
} catch (e: FileNotFoundException) {
   //catch logic
} catch (e: IOException) {
   //catch logic
} finally {
   /**
    * Clean up any open sockets when done
    * transferring or if an exception occurred.
    */
   socket.takeIf { it.isConnected }?.apply {
       close()
   }
}

Java

Context context = this.getApplicationContext();
String host;
int port;
int len;
Socket socket = new Socket();
byte buf[]  = new byte[1024];
...
try {
   /**
    * Create a client socket with the host,
    * port, and timeout information.
    */
   socket.bind(null);
   socket.connect((new InetSocketAddress(host, port)), 500);

   /**
    * Create a byte stream from a JPEG file and pipe it to the output stream
    * of the socket. This data is retrieved by the server device.
    */
   OutputStream outputStream = socket.getOutputStream();
   ContentResolver cr = context.getContentResolver();
   InputStream inputStream = null;
   inputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg"));
   while ((len = inputStream.read(buf)) != -1) {
       outputStream.write(buf, 0, len);
   }
   outputStream.close();
   inputStream.close();
} catch (FileNotFoundException e) {
   //catch logic
} catch (IOException e) {
   //catch logic
}

/**
* Clean up any open sockets when done
* transferring or if an exception occurred.
*/
finally {
   if (socket != null) {
       if (socket.isConnected()) {
           try {
               socket.close();
           } catch (IOException e) {
               //catch logic
           }
       }
   }
}