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

Wi-Fi Direct (P2P) umożliwia urządzeniom z odpowiednim sprzętem bezpośrednie łączenie się ze sobą przez Wi-Fi bez pośredniego punktu dostępu. Za pomocą tych interfejsów API możesz wykrywać inne urządzenia i nawiązywać z nimi połączenie, jeśli każde z nich obsługuje Wi-Fi P2P. Następnie możesz komunikować się za pomocą szybkiego połączenia na odległościach znacznie większych niż w przypadku połączenia Bluetooth. Jest to przydatne w przypadku aplikacji, które udostępniają dane użytkownikom, np. gier wieloosobowych lub aplikacji do udostępniania zdjęć.

Interfejsy Wi-Fi P2P składają się z tych głównych części:

  • Metody, które umożliwiają wykrywanie, wysyłanie próśb i nawiązywanie połączeń z innymi urządzeniami. Są one zdefiniowane w klasie WifiP2pManager.
  • Słuchacze, którzy powiadamiają o powodzeniu lub niepowodzeniu wywołań metody WifiP2pManager. Podczas wywoływania metod WifiP2pManager każda z nich może otrzymać konkretny odbiornik przekazany jako parametr.
  • Intencje, które powiadamiają o określonych zdarzeniach wykrytych przez platformę Wi-Fi P2P, takich jak utrata połączenia lub nowo wykryty węzeł.

Te 3 główne komponenty interfejsów API są często używane razem. Możesz na przykład podać WifiP2pManager.ActionListener w wywołaniu funkcji discoverPeers(), aby metody ActionListener.onSuccess()ActionListener.onFailure() mogły Cię powiadamiać. A WIFI_P2P_PEERS_CHANGED_ACTION Zamiar jest też rozgłaszany, jeśli metoda discoverPeers() wykryje, że lista urządzeń w pobliżu uległa zmianie.

Omówienie interfejsu API

Klasa WifiP2pManager udostępnia metody umożliwiające interakcję ze sprzętem Wi-Fi na urządzeniu, np. wyszukiwanie innych urządzeń i nawiązywanie z nimi połączenia. Dostępne są te opcje:

Tabela 1. Metody Wi-Fi P2P

Metoda Opis
initialize() Rejestruje aplikację w platformie Wi-Fi. Wywołaj tę metodę, zanim zaczniesz wywoływać inne metody Wi-Fi P2P.
connect() Nawiązuje połączenie peer-to-peer z urządzeniem o określonej konfiguracji.
cancelConnect() Anuluje trwające negocjacje w grupie peer-to-peer.
requestConnectInfo() Wysyła prośbę o informacje o połączeniu urządzenia.
createGroup() Tworzy grupę peer-to-peer, w której bieżące urządzenie jest właścicielem grupy.
removeGroup() Usuwa bieżącą grupę peer-to-peer.
requestGroupInfo() Wysyła prośbę o informacje o grupie peer-to-peer.
discoverPeers() Inicjuje wykrywanie peerów.
requestPeers() Wysyła prośbę o aktualną listę wykrytych urządzeń równorzędnych.

Metody WifiP2pManager umożliwiają przekazywanie detektora, dzięki czemu platforma Wi-Fi P2P może powiadamiać aktywność o stanie połączenia. Dostępne interfejsy odbiorników i odpowiednie WifiP2pManagerwywołania metod, które korzystają z odbiorników, opisano w tabeli 2.

Tabela 2. Detektory Wi-Fi P2P

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

Interfejsy API Wi-Fi P2P definiują intencje, które są rozgłaszane, gdy wystąpią określone zdarzenia Wi-Fi P2P, np. gdy zostanie wykryty nowy węzeł lub gdy zmieni się stan Wi-Fi urządzenia. Aby zarejestrować się w celu otrzymywania tych intencji w aplikacji, utwórz odbiornik transmisji, który będzie je obsługiwać:

Tabela 3. Intencje Wi-Fi P2P

Zamiar Opis
WIFI_P2P_CONNECTION_CHANGED_ACTION Wysyłany, gdy zmieni się stan połączenia Wi-Fi urządzenia.
WIFI_P2P_PEERS_CHANGED_ACTION Rozgłaszaj, gdy dzwonisz pod numer discoverPeers(). Zazwyczaj wywołujesz funkcję requestPeers(), aby uzyskać zaktualizowaną listę urządzeń równorzędnych, jeśli obsługujesz ten zamiar w swojej aplikacji.
WIFI_P2P_STATE_CHANGED_ACTION Przesyłanie, gdy Wi-Fi P2P jest włączone lub wyłączone na urządzeniu.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION rozgłaszanie informacji o zmianach w szczegółach urządzenia, np. w jego nazwie;

Tworzenie odbiornika transmisji dla intencji Wi-Fi P2P

Odbiornik transmisji umożliwia odbieranie intencji transmitowanych przez system Android, dzięki czemu aplikacja może reagować na interesujące ją zdarzenia. Podstawowe czynności związane z tworzeniem odbiornika transmisji do obsługi intencji Wi-Fi P2P są następujące:

  1. Utwórz klasę, która rozszerza klasę BroadcastReceiver. W konstruktorze klasy użyjesz parametrów WifiP2pManager, WifiP2pManager.Channel i aktywności, w której ten odbiornik transmisji będzie zarejestrowany. Dzięki temu odbiornik transmisji może wysyłać aktualizacje do aktywności, a w razie potrzeby mieć dostęp do sprzętu Wi-Fi i kanału komunikacji.

  2. W odbiorniku transmisji sprawdź intencje, które Cię interesują, w metodzie onReceive(). wykonywać niezbędne działania w zależności od otrzymanego zamiaru. Jeśli na przykład odbiornik transmisji otrzyma intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz wywołać metodę requestPeers(), aby uzyskać listę aktualnie wykrytych urządzeń równorzędnych.

Poniższy kod pokazuje, jak utworzyć typowy odbiornik transmisji. Odbiornik transmisji przyjmuje jako argumenty obiekt WifiP2pManager i aktywność. Używa tych 2 klas, aby odpowiednio wykonywać potrzebne działania, gdy odbiornik transmisji 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 i nowszym te intencje transmisji nie są trwałe:

WIFI_P2P_CONNECTION_CHANGED_ACTION
Aplikacje mogą używaćrequestConnectionInfo(),requestNetworkInfo() lub requestGroupInfo() do pobierania aktualnych informacji o połączeniu.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Aplikacje mogą używać requestDeviceInfo() do pobierania aktualnych informacji o połączeniu.

Tworzenie aplikacji Wi-Fi P2P

Tworzenie aplikacji Wi-Fi P2P obejmuje utworzenie i zarejestrowanie odbiornika transmisji dla aplikacji, wykrywanie urządzeń równorzędnych, łączenie się z urządzeniem równorzędnym i przesyłanie do niego danych. W sekcjach poniżej znajdziesz opis, jak to zrobić.

Konfiguracja początkowa

Zanim zaczniesz korzystać z interfejsów API Wi-Fi P2P, musisz się upewnić, że aplikacja ma dostęp do sprzętu i że urządzenie obsługuje protokół Wi-Fi P2P. Jeśli Wi-Fi P2P jest obsługiwane, możesz uzyskać instancję WifiP2pManager, utworzyć i zarejestrować odbiornik transmisji oraz zacząć korzystać z interfejsów API Wi-Fi P2P.

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

    <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 wymienionych wyżej uprawnień te interfejsy API wymagają też włączenia trybu lokalizacji:

  2. Sprawdź, czy Wi-Fi P2P jest włączone i obsługiwane. Dobrym miejscem do sprawdzenia tego jest odbiornik transmisji, gdy odbiera on intencję WIFI_P2P_STATE_CHANGED_ACTION. Powiadom swoją aktywność o stanie Wi-Fi 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 aktywności onCreate() uzyskaj instancję WifiP2pManager i zarejestruj aplikację w ramach Wi-Fi P2P, wywołując initialize(). Ta metoda zwraca obiekt WifiP2pManager.Channel, który służy do łączenia aplikacji z platformą Wi-Fi P2P. Utwórz też instancję odbiornika transmisji z obiektami WifiP2pManagerWifiP2pManager.Channel oraz odwołaniem do aktywności. Dzięki temu odbiornik transmisji może powiadamiać aktywność o interesujących zdarzeniach i odpowiednio ją aktualizować. Umożliwia też w razie potrzeby zmianę stanu 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 transmisji:

    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 transmisji w metodzie onResume() aktywności i wyrejestruj go w metodzie onPause() aktywności:

    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 uzyskasz WifiP2pManager.Channel i skonfigurujesz odbiornik transmisji, Twoja aplikacja będzie mogła wykonywać wywołania metod Wi-Fi P2P i odbierać intencje Wi-Fi P2P.

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

W kolejnych sekcjach opisujemy, jak wykonywać typowe działania, takie jak wykrywanie i łączenie się z innymi urządzeniami.

Odkrywanie aplikacji z grupy porównawczej

Wywołaj funkcję discoverPeers(), aby wykryć dostępne urządzenia w pobliżu, z którymi można się połączyć. Wywołanie tej funkcji jest asynchroniczne, a informacja o sukcesie lub niepowodzeniu jest przekazywana do aplikacji za pomocą funkcji onSuccess()onFailure(), jeśli utworzono WifiP2pManager.ActionListener. Metoda onSuccess() tylko informuje, że proces wykrywania się powiódł, i nie podaje żadnych informacji o rzeczywistych wykrytych urządzeniach, jeśli takie istnieją. 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 urządzenia równorzędne, system wyśle intencję WIFI_P2P_PEERS_CHANGED_ACTION, której możesz nasłuchiwać w odbiorniku transmisji, aby uzyskać listę urządzeń równorzędnych. Gdy aplikacja otrzyma intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz poprosić o listę wykrytych urządzeń równorzędnych za pomocą requestPeers(). Poniższy kod pokazuje, jak to skonfigurować.

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() jest też asynchroniczna i może powiadamiać o aktywności, gdy dostępna jest lista peerów z onPeersAvailable(), która jest zdefiniowana w interfejsie WifiP2pManager.PeerListListener. Metoda onPeersAvailable() zwraca obiekt WifiP2pDeviceList, po którym możesz iterować, aby znaleźć węzeł do połączenia.

Nawiązywanie kontaktu z innymi uczestnikami

Po uzyskaniu listy możliwych urządzeń równorzędnych i wybraniu urządzenia, z którym chcesz się połączyć, wywołaj metodę connect(), aby nawiązać połączenie. 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 powodzeniu lub niepowodzeniu 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 możesz przesyłać dane między urządzeniami z gniazdami. Podstawowe kroki przenoszenia danych są następujące:

  1. Tworzenie ServerSocket. To gniazdo czeka na połączenie od klienta na określonym porcie i blokuje się do momentu, aż to nastąpi, więc wykonaj tę czynność w wątku w tle.
  2. Utwórz klienta Socket. Klient używa adresu IP i portu gniazda serwera, aby połączyć się z urządzeniem serwera.
  3. Wysyłanie danych z klienta na serwer. Gdy gniazdo klienta połączy się z gniazdem serwera, możesz wysyłać dane z klienta na serwer za pomocą strumieni bajtów.
  4. Gniazdo serwera oczekuje na połączenie klienta (za pomocą metody accept()). To wywołanie blokuje działanie programu do momentu połączenia się klienta, więc wywołaj je w innym wątku. Po nawiązaniu połączenia urządzenie serwera może odbierać dane z klienta.

Poniższy przykład, zmodyfikowany na podstawie Wi-Fi P2P Demo, pokazuje, jak utworzyć komunikację gniazdową klient-serwer i przesyłać obrazy JPEG z klienta na serwer za pomocą usługi. Aby zobaczyć kompletny 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);
       }
   }
}

Na kliencie połącz się z gniazdem serwera za pomocą gniazda klienta i przenieś dane. W tym przykładzie przesyłany jest plik JPEG z systemu plików urządzenia klienta.

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
           }
       }
   }
}