Présentation du Wi-Fi Direct (peer-to-peer ou P2P)

Wi-Fi Direct (P2P) permet aux appareils équipés du matériel approprié de se connecter directement à chacun via le Wi-Fi sans point d’accès intermédiaire. Grâce à ces API, vous pouvez détecter d'autres appareils et s'y connecter dès lors que chacun d'eux est compatible avec le Wi-Fi P2P ; de communiquer via une connexion rapide sur des distances beaucoup plus longues qu'un Connexion Bluetooth. Ceci est utile pour les applications qui partagent des données entre comme un jeu multijoueur ou une application de partage de photos.

Les API Wi-Fi P2P se composent des éléments principaux suivants:

  • Les méthodes qui vous permettent de découvrir, de demander et de vous connecter à des pairs, qui sont définis dans le WifiP2pManager.
  • Les écouteurs vous permettent d'être averti de la réussite ou de l'échec Appels de méthode WifiP2pManager. Lorsque vous appelez des méthodes WifiP2pManager, chaque peut recevoir un écouteur spécifique transmis en tant que paramètre.
  • Intents qui vous informent d'événements spécifiques détectés par le réseau Wi-Fi P2P comme une perte de connexion ou un nouveau pair.

Vous utiliserez souvent ces trois composants principaux des API ensemble. Pour par exemple, vous pouvez fournir WifiP2pManager.ActionListener à un appel pour discoverPeers() afin que ActionListener.onSuccess() et ActionListener.onFailure() méthodes peuvent vous avertir. A WIFI_P2P_PEERS_CHANGED_ACTION l'intent est également diffusé si la méthode discoverPeers() découvre que le la liste des pairs a changé.

Présentation de l'API

La classe WifiP2pManager fournit des méthodes pour vous permettre d'interagir avec le du matériel Wi-Fi sur votre appareil pour effectuer des actions, comme découvrir des appareils similaires et s'y connecter. Les actions suivantes sont disponibles:

Tableau 1. Méthodes Wi-Fi P2P

Méthode Description
initialize() Enregistre l'application auprès du framework Wi-Fi. Appeler cet élément avant le appelant n'importe quelle autre méthode Wi-Fi P2P.
connect() Démarre une connexion pair à pair avec un appareil disposant de la configuration spécifiée.
cancelConnect() Annule toute négociation de groupe peer-to-peer en cours.
requestConnectInfo() Demande les informations de connexion d'un appareil.
createGroup() Crée un groupe peer-to-peer avec l'appareil actuel en tant que propriétaire du groupe.
removeGroup() Supprime le groupe peer-to-peer actuel.
requestGroupInfo() Demande des informations sur les groupes poste-à-poste.
discoverPeers() Lance la détection de pairs.
requestPeers() Demande la liste actuelle des pairs découverts.

WifiP2pManager méthode permet vous transmettez un écouteur, de sorte que le framework P2P Wi-Fi puisse notifier votre activité de l'état d'un appel. Les interfaces d'écouteur disponibles et les Appels de méthode WifiP2pManager qui utilisent les écouteurs sont décrits dans le tableau 2.

Tableau 2. Écouteurs Wi-Fi P2P

Interface de l'écouteur Actions associées
WifiP2pManager.ActionListener connect(), cancelConnect() createGroup(), removeGroup() et discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()

Les API Wi-Fi P2P définissent les intents qui sont diffusés lorsque certains réseaux Wi-Fi P2P se produisent, par exemple, lorsqu'un nouveau pair est détecté ou lorsque le Wi-Fi d'un appareil est détecté. les changements d'état. Vous pouvez vous inscrire pour recevoir ces intents dans votre application en Créer un broadcast receiver qui gère ces intents:

Tableau 3 : Intents Wi-Fi P2P

Intention Description
WIFI_P2P_CONNECTION_CHANGED_ACTION Annoncer lorsque l'état de la connexion Wi-Fi de l'appareil change.
WIFI_P2P_PEERS_CHANGED_ACTION Diffuser lorsque vous appelez discoverPeers(). Vous aurez généralement appelez requestPeers() pour obtenir une liste à jour des pairs si vous gérer cet intent dans votre application.
WIFI_P2P_STATE_CHANGED_ACTION Diffuser lorsque le Wi-Fi P2P est activé ou désactivé sur l'appareil.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION Diffuser lorsque les détails d'un appareil ont changé, tels que son nom.

Créer un broadcast receiver pour les intents Wi-Fi P2P

Un broadcast receiver vous permet de recevoir des intents diffusés par l'Android pour que votre application puisse répondre aux événements qui vous intéressent po. Étapes de base de la création d'un broadcast receiver afin de gérer le Wi-Fi P2P les intents sont les suivants:

  1. Créez une classe qui étend les BroadcastReceiver. Pour le cours vous utiliserez des paramètres pour WifiP2pManager, WifiP2pManager.Channel, et l'activité que ce broadcast receiver va être enregistré. Cela permet au broadcast receiver d'envoyer des mises à jour au d'activité, et ont accès au matériel Wi-Fi et à un système canal si nécessaire.

  2. Dans le broadcast receiver, recherchez les intents qui vous intéressent dans onReceive() . Effectuer toutes les actions nécessaires en fonction de l'intent reçues. Par exemple, si le broadcast receiver reçoit une WIFI_P2P_PEERS_CHANGED_ACTION, vous pouvez appeler l'intent requestPeers() pour obtenir la liste des pairs actuellement découverts.

Le code suivant montre comment créer un broadcast receiver type. La Le broadcast receiver utilise un objet WifiP2pManager et une activité comme arguments et utilise ces deux classes pour effectuer les actions nécessaires de façon appropriée le broadcast receiver reçoit un intent:

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

Sur les appareils équipés d'Android 10 ou version ultérieure, les intents de diffusion suivants sont non collant:

WIFI_P2P_CONNECTION_CHANGED_ACTION
Les applications peuvent utiliser requestConnectionInfo(), requestNetworkInfo() ou requestGroupInfo() pour récupérer les informations de connexion actuelles.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Les applications peuvent utiliser requestDeviceInfo() pour récupérer les informations de connexion actuelles.

Créer une application Wi-Fi P2P

La création d'une application Wi-Fi P2P implique la création et l'enregistrement d'une diffusion à votre application, découvrir des pairs, vous connecter à un pair et le transfert de données à un pair. Dans les sections suivantes, nous allons voir comment procéder.

Configuration initiale

Avant d'utiliser les API Wi-Fi P2P, vous devez vous assurer que votre application peut accéder au matériel et que l'appareil est compatible avec le protocole Wi-Fi P2P. Si Le Wi-Fi P2P est pris en charge, vous pouvez obtenir une instance de WifiP2pManager, créer puis enregistrez votre broadcast receiver et commencez à utiliser les API Wi-Fi P2P.

  1. Demander l'autorisation d'utiliser le matériel Wi-Fi sur l'appareil et déclarer que votre application dispose de la version minimale requise du SDK dans fichier manifeste:

    <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" />
    

    En plus des autorisations précédentes, les API suivantes nécessitent également l'accès aux données de localisation Mode à activer:

  2. Vérifiez que le Wi-Fi P2P est activé et compatible. Un bon endroit pour vérifier cela est dans votre broadcast receiver lorsqu'il reçoit Intent WIFI_P2P_STATE_CHANGED_ACTION. M'avertir en cas d'activité de la connexion Wi-Fi P2P et réagir en conséquence:

    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. Dans les onCreate() , obtenez une instance de WifiP2pManager et enregistrez votre application avec le framework Wi-Fi P2P en appelant initialize(). Cette méthode renvoie WifiP2pManager.Channel, qui permet de connecter votre application au Framework Wi-Fi P2P. Vous devez également créer une instance de votre broadcast récepteur avec les objets WifiP2pManager et WifiP2pManager.Channel, ainsi que en faisant référence à votre activité. Cela permet à votre broadcast receiver signaler les événements intéressants à votre activité et les mettre à jour en conséquence. Il y a aussi vous permet de contrôler l'état du Wi-Fi de l'appareil si nécessaire:

    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. Créer un filtre d'intent et ajouter les mêmes intents que votre broadcast receiver recherche les éléments suivants:

    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. Enregistrez le broadcast receiver dans méthode onResume() de votre l'activité et annule son enregistrement Méthode onPause() de votre activité:

    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. Lorsque vous avez reçu une WifiP2pManager.Channel et que vous avez configuré une diffusion votre application peut effectuer des appels Wi-Fi en mode P2P et recevoir des Intents P2P

  7. Mettez en œuvre votre application à l'aide des fonctionnalités Wi-Fi P2P en appelant la méthode dans WifiP2pManager.

Les sections suivantes décrivent comment effectuer des actions courantes, comme découvrir et de se connecter aux pairs.

Découvrir des applications similaires

Appelez discoverPeers() pour détecter les pairs disponibles à la plage et disponibles. pour la connexion. L'appel de cette fonction est asynchrone et aboutit ou l'échec est communiqué à votre application avec onSuccess() et onFailure(). si vous avez créé un WifiP2pManager.ActionListener. La méthode onSuccess() uniquement vous informe que le processus de découverte a réussi et ne fournit des informations sur les pairs réels qu’il a découverts, le cas échéant. Les éléments suivants : l'exemple de code ci-dessous montre comment procéder.

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) {
       ...
   }
});

Si le processus de découverte réussit et détecte les pairs, le système diffuse le L'intent WIFI_P2P_PEERS_CHANGED_ACTION, que vous pouvez écouter dans une annonce "receiver" pour obtenir la liste des pairs. Lorsque votre application reçoit le WIFI_P2P_PEERS_CHANGED_ACTION, vous pouvez demander la liste des est appairé à requestPeers(). Le code suivant montre comment procéder.

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

La méthode requestPeers() est également asynchrone et peut notifier votre activité. lorsqu'une liste de pairs est disponible avec onPeersAvailable(), définie dans l'interface WifiP2pManager.PeerListListener. La La méthode onPeersAvailable() vous fournit une WifiP2pDeviceList, qui vous pouvez itérer pour trouver le pair auquel vous connecter.

Se connecter à des applications similaires

Une fois que vous avez obtenu une liste de pairs possibles et sélectionné un appareil à connecter appelez la méthode connect() pour vous connecter à l'appareil. Cet appel de méthode nécessite un WifiP2pConfig contenant des informations sur l'appareil auquel se connecter. WifiP2pManager.ActionListener peut vous informer de la réussite de la connexion ou l'échec. Le code suivant vous montre comment créer une connexion à un appareil.

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

Transférer des données

Une fois la connexion établie, vous pouvez transférer des données entre les appareils avec sockets. Les étapes de base du transfert de données sont les suivantes:

  1. Créez un ServerSocket. Ce socket attend une connexion d'un client sur un port spécifié et se bloque jusqu'à alors faites-le dans un thread d'arrière-plan.
  2. Créez un client Socket. Le client utilise Adresse IP et port du socket serveur pour se connecter au périphérique serveur.
  3. Envoyer des données du client au serveur. Lorsque le socket client réussit se connecte au socket serveur, vous pouvez envoyer des données du client au avec des flux d'octets.
  4. Le socket serveur attend une connexion client (avec le accept()). Cet appel jusqu'à ce qu'un client se connecte. Vous devez donc l'appeler dans un autre thread. Lorsqu'un connexion se produit, le périphérique serveur peut recevoir les données du client.

L'exemple suivant, modifié à partir du schéma Wi-Fi P2P Démonstration, vous montre comment créer cette communication de socket client-serveur et transférer Images JPEG d'un client vers un serveur avec un service. Pour une formation complète compilez et exécutez la démonstration.

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

Sur le client, connectez-vous au socket serveur avec un socket client et transférez données. Cet exemple transfère un fichier JPEG sur le système de fichiers de l'appareil client.

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