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

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

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

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

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

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

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

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

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

ה-methods של WifiP2pManager מאפשרות להעביר מאזין, כדי שה-framework של 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 Wi-Fi 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 Wi-Fi state changing
       }
   }
}

במכשירים עם Android מגרסה 10 ואילך, כוונות השידור הבאות לא נשמרות:

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 מופעל ונתמך. מקום טוב לבדוק את זה הוא במקלט השידור (broadcast receiver) כשהוא מקבל את ה-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 -> {
                   // Wi-Fi 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) {
           // Wi-Fi P2P is enabled
       } else {
           // Wi-Fi P2P is not enabled
       }
    }
    ...
    }
    
  3. ב-method onCreate() של הפעילות, מקבלים מופע של WifiP2pManager ורושמים את האפליקציה במסגרת Wi-Fi P2P באמצעות קריאה ל-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) {
       ...
   }
});

אם תהליך הגילוי מצליח ומזהה עמיתים, המערכת משדרת את שידור המערכת 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 Wi-Fi 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, שאפשר לעבור עליו כדי למצוא את המכשיר שאליו רוצים להתחבר.

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

אחרי שקיבלתם רשימה של עמיתים אפשריים ובחרתם מכשיר להתחבר אליו, צריך להפעיל את השיטה 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. שקע השרת (server socket) ממתין לחיבור של לקוח (באמצעות השיטה 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);
       }
   }
}

בצד הלקוח, מתחברים לשקע השרת (server socket) באמצעות שקע לקוח (client socket) ומעבירים נתונים. בדוגמה הזו מועבר קובץ 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
           }
       }
   }
}