Wi-Fi Direct(P2P) 개요

Wi-Fi Direct(P2P)를 사용하면 적절한 하드웨어가 있는 기기를 중간 액세스 포인트 없이 Wi-Fi를 통해 서로 직접 연결할 수 있습니다. 이러한 API를 사용하면 각 기기가 Wi-Fi P2P를 지원할 때 다른 기기를 검색하여 연결한 다음 블루투스 연결보다 훨씬 긴 거리의 빠른 연결을 통해 통신할 수 있습니다. 이는 멀티플레이어 게임이나 사진 공유 애플리케이션과 같이 사용자 간에 데이터를 공유하는 애플리케이션에 유용합니다.

Wi-Fi P2P API는 다음과 같은 주요 부분으로 구성됩니다.

  • 피어를 검색, 요청, 연결할 수 있는 메서드로, WifiP2pManager 클래스에 정의되어 있습니다.
  • WifiP2pManager 메서드 호출의 성공 또는 실패에 관한 알림을 받을 수 있는 리스너 WifiP2pManager 메서드를 호출할 때 각 메서드는 매개변수로 전달된 특정 리스너를 수신할 수 있습니다.
  • Wi-Fi P2P 프레임워크에서 감지한 특정 이벤트(예: 연결 끊김 또는 새로 검색된 피어)를 알려주는 인텐트

이 세 가지 주요 구성요소를 함께 사용하는 경우가 많습니다. 예를 들어 discoverPeers() 호출에 WifiP2pManager.ActionListener를 제공하여 ActionListener.onSuccess()ActionListener.onFailure() 메서드에서 알림을 보낼 수 있습니다. discoverPeers() 메서드가 동종 앱 목록이 변경된 것을 발견하면 WIFI_P2P_PEERS_CHANGED_ACTION 인텐트도 브로드캐스트됩니다.

API 개요

WifiP2pManager 클래스는 기기의 Wi-Fi 하드웨어와 상호작용하여 검색 및 동종 기기에 연결하는 등의 작업을 할 수 있는 메서드를 제공합니다. 다음과 같은 작업이 가능합니다.

표 1. Wi-Fi P2P 방법

방법 설명
initialize() Wi-Fi 프레임워크로 애플리케이션을 등록합니다. 다른 Wi-Fi P2P 메서드를 호출하기 전에 이 메서드를 호출합니다.
connect() 지정된 구성이 있는 기기와 P2P 연결을 시작합니다.
cancelConnect() 모든 진행 중인 P2P 그룹 협상을 취소합니다.
requestConnectInfo() 기기의 연결 정보를 요청합니다.
createGroup() 현재 기기를 그룹 소유주로 하는 P2P 그룹을 생성합니다.
removeGroup() 현재 P2P 그룹을 삭제합니다.
requestGroupInfo() P2P 그룹 정보를 요청합니다.
discoverPeers() 피어 검색을 시작합니다.
requestPeers() 검색된 피어의 최신 목록을 요청합니다.

WifiP2pManager 메서드를 사용하면 리스너를 전달하여 Wi-Fi P2P 프레임워크가 호출 상태를 활동에 알릴 수 있습니다. 사용 가능한 리스너 인터페이스와 리스너를 사용하는 상응하는 WifiP2pManager 메서드 호출은 표 2에 설명되어 있습니다.

표 2. Wi-Fi P2P 리스너

리스너 인터페이스 연결된 작업
WifiP2pManager.ActionListener connect(), cancelConnect(), createGroup(), removeGroup(), discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()

Wi-Fi P2P API는 특정 Wi-Fi P2P 이벤트가 발생할 때(예: 새 피어가 검색되거나 기기의 Wi-Fi 상태가 변경될 때) 브로드캐스트되는 인텐트를 정의합니다. 이러한 인텐트를 처리하는 broadcast receiver를 만들어 애플리케이션에서 이러한 인텐트를 수신하도록 등록할 수 있습니다.

표 3. Wi-Fi P2P 인텐트

인텐트 설명
WIFI_P2P_CONNECTION_CHANGED_ACTION 기기의 Wi-Fi 연결 상태가 변경될 때 브로드캐스트합니다.
WIFI_P2P_PEERS_CHANGED_ACTION discoverPeers()님을 호출할 때 방송합니다. 애플리케이션에서 이 인텐트를 처리하는 경우 일반적으로 requestPeers()를 호출하여 업데이트된 동종 앱 목록을 가져옵니다.
WIFI_P2P_STATE_CHANGED_ACTION 기기에서 Wi-Fi P2P가 활성화되었거나 비활성화되었는지 브로드캐스트합니다.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 기기 이름 등 기기 세부정보가 변경되었을 때 브로드캐스트합니다.

Wi-Fi P2P 인텐트의 Broadcast Receiver 만들기

broadcast receiver를 사용하면 Android 시스템에서 브로드캐스트한 인텐트를 수신할 수 있으므로 애플리케이션에서 관심 있는 이벤트에 응답할 수 있습니다. Wi-Fi P2P 인텐트를 처리할 broadcast receiver를 생성하는 기본 단계는 다음과 같습니다.

  1. BroadcastReceiver 클래스를 확장하는 클래스를 만듭니다. 클래스 생성자의 경우 WifiP2pManager, WifiP2pManager.Channel, 그리고 이 broadcast receiver가 등록될 활동의 매개변수를 사용합니다. 이렇게 하면 broadcast receiver가 활동에 업데이트를 보낼 수 있을 뿐만 아니라 필요한 경우 Wi-Fi 하드웨어와 통신 채널에 액세스할 수 있습니다.

  2. broadcast receiver에서 onReceive() 메서드에 관심이 있는 인텐트를 확인합니다. 수신된 인텐트에 따라 필요한 작업을 실행합니다. 예를 들어 broadcast receiver가 WIFI_P2P_PEERS_CHANGED_ACTION 인텐트를 수신하면 requestPeers() 메서드를 호출하여 현재 검색된 동종 앱의 목록을 가져올 수 있습니다.

다음 코드는 일반적인 Broadcast Receiver를 생성하는 방법을 보여줍니다. broadcast receiver는 WifiP2pManager 객체와 활동을 인수로 취하고 이 두 클래스를 사용하여 broadcast receiver가 인텐트를 수신할 때 필요한 작업을 적절하게 실행합니다.

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 이상을 실행하는 기기에서는 다음 브로드캐스트 인텐트가 고정되지 않습니다.

WIFI_P2P_CONNECTION_CHANGED_ACTION
애플리케이션은 requestConnectionInfo(), requestNetworkInfo() 또는 requestGroupInfo()를 사용하여 현재 연결 정보를 검색할 수 있습니다.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
애플리케이션은 requestDeviceInfo()를 사용하여 현재 연결 정보를 검색할 수 있습니다.

Wi-Fi P2P 애플리케이션 만들기

Wi-Fi P2P 애플리케이션을 만들려면 애플리케이션의 broadcast receiver를 생성 및 등록하고 피어를 검색하고 피어에 연결하고 데이터를 피어에 전송합니다. 다음 섹션에서는 그 방법을 설명합니다.

초기 설정

Wi-Fi P2P API를 사용하기 전에 애플리케이션이 하드웨어에 액세스할 수 있고 기기가 Wi-Fi P2P 프로토콜을 지원하는지 확인해야 합니다. Wi-Fi P2P가 지원되는 경우 WifiP2pManager의 인스턴스를 가져오고 broadcast receiver를 생성 및 등록하고 Wi-Fi P2P API를 사용할 수 있습니다.

  1. 기기에서 Wi-Fi 하드웨어를 사용하기 위한 권한을 요청하고 Android 매니페스트에서 애플리케이션의 최소 SDK 버전이 올바르도록 선언합니다.

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

    위의 권한 외에 다음 API에서는 위치 모드를 사용 설정해야 합니다.

  2. Wi-Fi P2P가 켜져 있고 지원되는지 확인합니다. WIFI_P2P_STATE_CHANGED_ACTION 인텐트를 수신할 때 broadcast receiver에서 이를 확인하는 것이 좋습니다. 활동에 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. 활동의 onCreate() 메서드에서 WifiP2pManager의 인스턴스를 가져오고 initialize()를 호출하여 Wi-Fi P2P 프레임워크에 애플리케이션을 등록합니다. 이 메서드는 애플리케이션을 Wi-Fi P2P 프레임워크에 연결하는 데 사용되는 WifiP2pManager.Channel를 반환합니다. WifiP2pManagerWifiP2pManager.Channel 객체로 활동 참조와 함께 broadcast receiver의 인스턴스도 만들어야 합니다. 이렇게 하면 broadcast receiver가 관심 있는 이벤트를 내 활동에 알리고 적절하게 업데이트할 수 있습니다. 또한 필요한 경우 기기의 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. 인텐트 필터를 만들고 broadcast receiver가 확인하는 것과 동일한 인텐트를 추가합니다.

    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. 활동의 onResume() 메서드에서 broadcast receiver를 등록하고 활동의 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를 가져오고 broadcast receiver를 설정하면 애플리케이션에서 Wi-Fi P2P 메서드를 호출하고 Wi-Fi P2P 인텐트를 수신할 수 있습니다.

  7. WifiP2pManager에서 메서드를 호출하여 Wi-Fi P2P 기능을 사용하여 애플리케이션을 구현합니다.

다음 섹션에서는 피어 검색 및 연결과 같은 일반적인 작업을 수행하는 방법을 설명합니다.

피어 검색

discoverPeers()를 호출하여 범위 내에 있고 연결에 사용 가능한 피어를 감지합니다. 이 함수는 비동기식으로 호출되며 WifiP2pManager.ActionListener를 만든 경우 성공 또는 실패가 onSuccess()onFailure()를 통해 애플리케이션에 전달됩니다. 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 인텐트를 브로드캐스트합니다. 이 인텐트는 broadcast receiver에서 수신 대기하여 동종 기기 목록을 가져올 수 있습니다. 애플리케이션이 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);
   }
}

requestPeers() 메서드도 비동기식이며 WifiP2pManager.PeerListListener 인터페이스에 정의된 onPeersAvailable()를 통해 동종 앱 목록을 사용할 수 있을 때 활동에 알릴 수 있습니다. onPeersAvailable() 메서드는 WifiP2pDeviceList를 제공하며 이 과정을 반복하여 연결할 피어를 찾을 수 있습니다.

피어 연결

가능한 동종 앱 목록을 가져오고 연결할 기기를 선택했으면 connect() 메서드를 호출하여 기기에 연결합니다. 이 메서드 호출에는 연결할 기기에 관한 정보가 포함된 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. 서버 소켓이 클라이언트 연결을 기다립니다 (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
           }
       }
   }
}