סקירה כללית על Wi-Fi ישיר (מקצה לקצה (P2P) או P2P)

Wi-Fi ישיר (P2P) מאפשר למכשירים עם החומרה המתאימה להתחבר ישירות זה לזה באמצעות Wi-Fi, בלי נקודת גישה מתווכת. באמצעות ממשקי ה-API האלה, אתם יכולים לגלות מכשירים אחרים ולהתחבר אליהם כשכל מכשיר תומך ב-Wi-Fi P2P, ואז לתקשר בחיבור מהיר למרחקים ארוכים בהרבה מחיבור Bluetooth. האפשרות הזו שימושית לאפליקציות שמשתפות נתונים בין משתמשים, כמו משחק רב-משתתפים או אפליקציה לשיתוף תמונות.

ממשקי ה-API של Wi-Fi P2P מורכבים מהחלקים העיקריים הבאים:

  • שיטות שמאפשרות לגלות עמיתים, לשלוח להם בקשות ולהתחבר אליהם, שמוגדרות במחלקה WifiP2pManager.
  • מאזינים שמאפשרים לכם לקבל התראות על הצלחה או כישלון של הפעלות method של WifiP2pManager. כשמפעילים שיטות של WifiP2pManager, כל שיטה יכולה לקבל מאזין ספציפי שמועבר כפרמטר.
  • אינטנטים שמודיעים לכם על אירועים ספציפיים שזוהו על ידי מסגרת ה-Wi-Fi P2P, כמו חיבור שהתנתק או עמית חדש שהתגלה.

בדרך כלל משתמשים בשלושת הרכיבים העיקריים האלה של ממשקי ה-API ביחד. לדוגמה, אפשר לספק WifiP2pManager.ActionListener לקריאה ל-discoverPeers() כדי שהשיטות ActionListener.onSuccess() ו-ActionListener.onFailure() יוכלו לשלוח לכם התראות. ה-intent מסוג WIFI_P2P_PEERS_CHANGED_ACTION משודר גם אם ה-method‏ discoverPeers() מגלה שרשימת ה-peers השתנתה.

סקירה כללית על ממשקי API

הכיתה WifiP2pManager מספקת שיטות שמאפשרות לכם ליצור אינטראקציה עם חומרת ה-Wi-Fi במכשיר כדי לבצע פעולות כמו גילוי עמיתים והתחברות אליהם. אלה הפעולות הזמינות:

טבלה 1. שיטות Wi-Fi P2P

שיטה תיאור
initialize() רושמת את האפליקציה במסגרת ה-Wi-Fi. צריך להפעיל את ה-method הזו לפני שמפעילים כל method אחרת של Wi-Fi P2P.
connect() הפעלת חיבור בין מכשירים עם מכשיר בהגדרה שצוינה.
cancelConnect() מבטל כל משא ומתן קבוצתי מתמשך בין עמיתים.
requestConnectInfo() בקשת פרטי החיבור של המכשיר.
createGroup() יוצרת קבוצת רשתות שכנות (P2P) שבה המכשיר הנוכחי הוא הבעלים של הקבוצה.
removeGroup() הסרת הקבוצה הנוכחית של רשתות שכנות (peering).
requestGroupInfo() בקשה לקבלת מידע על קבוצת רשתות שכנות (peering).
discoverPeers() מתחיל את תהליך הגילוי של עמיתים.
requestPeers() בקשה לקבלת הרשימה הנוכחית של עמיתים שזוהו.

השיטות של WifiP2pManager מאפשרות להעביר מאזין, כדי שמסגרת ה-Wi-Fi P2P תוכל להודיע לפעילות על סטטוס השיחה. בטבלה 2 מתוארים ממשקי המאזינים הזמינים והקריאות המתאימות ל-method‏ WifiP2pManager שמשתמשות במאזינים.

טבלה 2. רכיבי listener של Wi-Fi P2P

ממשק Listener פעולות משויכות
WifiP2pManager.ActionListener ‫connect(),‏ cancelConnect(),‏ createGroup(),‏ removeGroup() וגם discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()

ממשקי ה-API של Wi-Fi P2P מגדירים כוונות שמשודרות כשאירועים מסוימים של Wi-Fi P2P מתרחשים, למשל כשמזוהה עמית חדש או כשמצב ה-Wi-Fi של המכשיר משתנה. כדי להירשם לקבלת ה-Intent האלה באפליקציה, צריך ליצור מקלט שידור שמטפל ב-Intent האלה:

טבלה 3. ‫Wi-Fi P2P Intents

הרציונל תיאור
WIFI_P2P_CONNECTION_CHANGED_ACTION שידור כשמצב החיבור של המכשיר ל-Wi-Fi משתנה.
WIFI_P2P_PEERS_CHANGED_ACTION הודעה לכולם כשמתקשרים אל discoverPeers(). בדרך כלל תתבצע קריאה ל-requestPeers() כדי לקבל רשימה מעודכנת של עמיתים אם ה-intent הזה מטופל באפליקציה שלך.
WIFI_P2P_STATE_CHANGED_ACTION שידור כשה-Wi-Fi P2P מופעל או מושבת במכשיר.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION שידור כשפרטי המכשיר משתנים, למשל שם המכשיר.

יצירת מקלט שידור ל-intent של Wi-Fi P2P

מקלט שידור מאפשר לכם לקבל כוונות שמשודרות על ידי מערכת Android, כדי שהאפליקציה שלכם תוכל להגיב לאירועים שמעניינים אתכם. אלה השלבים הבסיסיים ליצירת מקלט שידור לטיפול ב-Intent של Wi-Fi P2P:

  1. יוצרים מחלקה שמרחיבה את המחלקה BroadcastReceiver. ב-constructor של המחלקה, צריך להשתמש בפרמטרים של WifiP2pManager, של WifiP2pManager.Channel ושל הפעילות שבה נרשם מקלט השידור הזה. כך מקלט השידור יכול לשלוח עדכונים לפעילות, וגם לגשת לחומרת ה-Wi-Fi ולערוץ תקשורת במקרה הצורך.

  2. במקלט השידור, בודקים את ה-intent שמעניינים אתכם ב-method‏ onReceive(). מבצעים את הפעולות הנדרשות בהתאם ל-Intent שהתקבל. לדוגמה, אם מקלט השידור מקבל Intent מסוג WIFI_P2P_PEERS_CHANGED_ACTION, אפשר להפעיל את ה-method‏ requestPeers() כדי לקבל רשימה של המכשירים שזוהו כרגע.

הקוד הבא מראה איך ליצור מקלט שידור אופייני. מקלט השידור מקבל אובייקט WifiP2pManager ופעילות כארגומנטים, ומשתמש בשתי המחלקות האלה כדי לבצע את הפעולות הנדרשות כשמקלט השידור מקבל 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
       }
   }
}

במכשירים עם Android מגרסה 10 ואילך, ה-broadcast intents הבאים הם non-sticky:

WIFI_P2P_CONNECTION_CHANGED_ACTION
אפליקציות יכולות להשתמש ב-requestConnectionInfo(), ב-requestNetworkInfo() או ב-requestGroupInfo() כדי לאחזר את פרטי החיבור הנוכחיים.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
אפליקציות יכולות להשתמש ב-requestDeviceInfo() כדי לאחזר את פרטי החיבור הנוכחיים.

יצירת אפליקציית Wi-Fi P2P

כדי ליצור אפליקציית Wi-Fi P2P, צריך ליצור ולרשום מקלט שידור לאפליקציה, לגלות עמיתים, להתחבר לעמית ולהעביר נתונים לעמית. בקטעים הבאים מוסבר איך לעשות את זה.

הגדרה ראשונית

לפני שמשתמשים בממשקי ה-API של Wi-Fi P2P, צריך לוודא שלאפליקציה יש גישה לחומרה ושהמכשיר תומך בפרוטוקול Wi-Fi P2P. אם יש תמיכה ב-Wi-Fi P2P, אתם יכולים לקבל מופע של WifiP2pManager, ליצור ולרשום את מקלט השידור שלכם ולהתחיל להשתמש בממשקי ה-API של Wi-Fi P2P.

  1. בקשו הרשאה להשתמש בחומרת ה-Wi-Fi במכשיר והצהירו על כך שהאפליקציה שלכם כוללת את גרסת ה-SDK המינימלית הנכונה במניפסט של Android:

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

    בנוסף להרשאות שלמעלה, ממשקי ה-API הבאים דורשים גם הפעלה של מצב מיקום:

  2. בודקים אם Wi-Fi P2P מופעל ונתמך. מקום טוב לבדוק את זה הוא במקלט השידור, כשהוא מקבל את ה-intent‏ WIFI_P2P_STATE_CHANGED_ACTION. מודיעים לפעילות על מצב ה-Wi-Fi P2P ומגיבים בהתאם:

    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. ב-method onCreate() של הפעילות, מקבלים מופע של WifiP2pManager ורושמים את האפליקציה ב-Wi-Fi P2P framework באמצעות קריאה ל-initialize(). ה-method הזה מחזיר את WifiP2pManager.Channel, שמשמש לחיבור האפליקציה למסגרת Wi-Fi P2P. צריך גם ליצור מופע של מקלט השידור עם האובייקטים WifiP2pManager ו-WifiP2pManager.Channel, יחד עם הפניה לפעילות. כך מקלט השידור יכול להודיע לפעילות על אירועים מעניינים ולעדכן אותה בהתאם. היא גם מאפשרת לשנות את מצב ה-Wi-Fi של המכשיר במקרה הצורך:

    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. יוצרים מסנן Intent ומוסיפים את אותם אובייקטי Intent שמקלט השידור בודק:

    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. רושמים את מקלט השידור ב-method‏ onResume() של הפעילות, ומבטלים את הרישום ב-method‏ 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. אחרי שמקבלים WifiP2pManager.Channel ומגדירים מקלט שידורים, האפליקציה יכולה לבצע הפעלות method של Wi-Fi P2P ולקבל Intent של Wi-Fi P2P.

  7. מטמיעים את האפליקציה באמצעות התכונות של Wi-Fi P2P, על ידי קריאה לשיטות ב-WifiP2pManager.

בקטעים הבאים מוסבר איך לבצע פעולות נפוצות כמו גילוי עמיתים והתחברות אליהם.

גילוי מתחרים

קוראים ל-discoverPeers() כדי לזהות מכשירים זמינים בטווח שאפשר להתחבר אליהם. הקריאה לפונקציה הזו היא אסינכרונית, והצלחה או כישלון מועברים לאפליקציה באמצעות onSuccess() ו-onFailure() אם יצרתם WifiP2pManager.ActionListener. ה-method‏ onSuccess() רק מודיעה שתהליך הגילוי הצליח, ולא מספקת מידע על המכשירים שהיא גילתה, אם בכלל. בדוגמת הקוד הבאה אפשר לראות איך מגדירים את זה.

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

אם תהליך הגילוי מצליח ומזהה עמיתים, המערכת משדרת את ה-intent‏ WIFI_P2P_PEERS_CHANGED_ACTION, שאפשר להאזין לו במקלט שידור כדי לקבל רשימה של עמיתים. כשהאפליקציה מקבלת את ה-intent‏ WIFI_P2P_PEERS_CHANGED_ACTION, אפשר לבקש רשימה של המכשירים שזוהו באמצעות requestPeers(). הקוד הבא מראה איך להגדיר את זה.

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

ה-method requestPeers() היא גם אסינכרונית ויכולה להודיע לפעילות שלכם כשרשימת משתתפים זמינה באמצעות onPeersAvailable(), שמוגדרת בממשק WifiP2pManager.PeerListListener. ה-method‏ onPeersAvailable() מספקת WifiP2pDeviceList, שאפשר לעבור עליו כדי למצוא את המכשיר שאליו רוצים להתחבר.

חיבור לעמיתים

אחרי שמקבלים רשימה של עמיתים אפשריים ובוחרים מכשיר להתחבר אליו, קוראים ל-method‏ connect() כדי להתחבר למכשיר. הפעלת ה-method הזו דורשת אובייקט WifiP2pConfig שמכיל מידע על המכשיר שאליו רוצים להתחבר. WifiP2pManager.ActionListener יכול להודיע לכם אם החיבור הצליח או נכשל. הקוד הבא מראה איך ליצור חיבור למכשיר.

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

העברת הנתונים

אחרי שהחיבור נוצר, אפשר להעביר נתונים בין המכשירים באמצעות שקעים. אלה השלבים הבסיסיים להעברת נתונים:

  1. יוצרים ServerSocket. השקע הזה ממתין לחיבור מלקוח ביציאה שצוינה ונחסם עד שזה קורה, לכן צריך לעשות את זה בשרשור ברקע.
  2. יוצרים לקוח Socket. הלקוח משתמש בכתובת ה-IP וביציאה של שקע השרת כדי להתחבר למכשיר השרת.
  3. שולחים נתונים מהלקוח לשרת. כשהחיבור בין שקע הלקוח לשקע השרת מצליח, אפשר לשלוח נתונים מהלקוח לשרת באמצעות זרמי בייטים.
  4. שקע השרת ממתין לחיבור של לקוח (באמצעות ה-method‏ accept()). הקריאה הזו חוסמת עד שהלקוח מתחבר, אז צריך להפעיל אותה בשרשור אחר. כשהחיבור נוצר, מכשיר השרת יכול לקבל את הנתונים מהלקוח.

בדוגמה הבאה, ששונתה מהדגמה של Wi-Fi P2P, מוסבר איך ליצור את התקשורת הזו בין שקע הלקוח לשקע השרת, ואיך להעביר תמונות JPEG מלקוח לשרת באמצעות שירות. כדי לראות דוגמה מלאה, אפשר לקמפל ולהריץ את ההדגמה.

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

בצד הלקוח, מתחברים לשקע השרת באמצעות שקע לקוח ומעבירים נתונים. בדוגמה הזו מועבר קובץ JPEG במערכת הקבצים של מכשיר הלקוח.

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