티스토리 뷰

개발/ANDROID

와이파이 다이렉트

카렐 2018. 11. 8. 11:17




업무에서 와이파이 다이렉트를 통해서 파일을 송신해야할 일이 생겼습니다.


그래서 구글링을 해서 예제를 찾았는데, 막상 찾은 예제들이 제대로 동작도 안하고 소스 파악도 힘들었습니다.

 - 구글링을 통해 찾을수 있는 예제(https://github.com/ahmontero/wifi-direct-demo) ; 이런 종류의 예제가 많이 보입니다.


그래서 나름대로 정리해서 이미지를 전송하는 샘플을 하나 만들어봤는데요,

혹시 저와같은 고생(?) 하시는분들에게 도움이 되길 바랍니다.


아래는 제가 만든 와이파이 샘플의 주소입니다.

https://github.com/karrel84/WifiDirectSample


AndroidManifest.xml

와이파이 다이렉트를 이용하기위해서는 

<uses-feature
android:name="android.hardware.wifi.direct"
android:required="true" />

기능 사용이 필요합니다.

그리고 제 예제에서는 아래와같은 권한이 필요했습니다.

<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" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

이중에 WRITE_EXTERNAL_STORAGE 와 ACCESS_COARSE_LOCATION 권한은 코드상에서 요청해줘야합니다.

private fun checkPermission() {
TedPermission.with(this)
.setPermissionListener(permissionlistener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION

)
.check()
}

특히 다른것보다 

Manifest.permission.ACCESS_COARSE_LOCATION

권한이 필요한데, 구글링을 해서 얻은 샘플에서는 위의 권한을 요청하고 있지 않습니다.

그래서 6.0 미만의 안드로이드 기기에서는 검색된 기기가 나오지 않는 현상이 발생했고 이 부분때문에 조금 삽질을 하게되었습니다. (권한 문제일거라는 생각을 하면서도 삽질 ㅠ)


WifiDirectBroadcastReceiver.kt

와이파이의 상태 등에 대해서 수신받을 수 있는 브로드캐스트 리시버를 만들어서 등록해주어야 합니다.

수신 받을 액션들은 아래와 같습니다.

private fun setupIntentFilter() {
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)
}


WIFI_P2P_STATE_CHANGED_ACTION

 - Wi-Fi p2p의 활성화 여부를 나타내는 브로드캐스트 동작입니다. 추가 EXTRA_WIFI_STATE는 상태 정보를 int로 제공합니다.

WIFI_P2P_PEERS_CHANGED_ACTION

- 사용 가능한 피어 목록이 변경되었음을 나타내는 브로드캐스트 의도 작업입니다. 피어 검색, 손실 또는 업데이트의 결과로 보낼 있습니다.


WIFI_P2P_CONNECTION_CHANGED_ACTION

- Wi-Fi p2p 연결 상태가 변경되었음을 나타내는 브로드캐스트 의도 작업입니다. 하나의 추가 EXTRA_WIFI_P2P_INFO WifiP2pInfo 개체 형식의 p2p 연결 정보를 제공합니다. 다른 추가 EXTRA_NETWORK_INFO 네트워크 정보를 NetworkInfo 형식으로 제공합니다. 번째 추가 정보는 그룹의 세부 정보를 제공합니다.


WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

 -  디바이스 세부 정보가 변경되었음을 나타내는 브로드캐스트 의도 작업입니다.


참고 : WifiP2pManager


WifiP2pManager

WifiP2pManager 를 통해서 주변의 peer 를 탐색하거나 원하는 피어로 연결 요청이 가능합니다.

private fun setupWifiP2pManager() {
manager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
channel = manager?.initialize(this, mainLooper, null)
}

WifiP2pInfo

Wifi 다이렉트로 연결이 완료되면 WIFI_P2P_CONNECTION_CHANGED_ACTION 액션을 통해서 WifiP2pInfo를 받을 수 있습니다.

WifiP2pInfo 에는 와이파이 그룹에서의 owner인지 확인해주는 플래그등을 참조할 수 있습니다.

이 예제에서는(다른 구글 예제와 마찬가지로..) owner 가 서버가되고 owner가 아니면 클라이언트가 외어서 클라이언트 -> 오너 로 사진을 전송하는 방식으로 구현하였습니다.


ServerFragment.kt

서버를 구현합니다. 약속된 8988 포트를 통해서 클라이언트가 송신하는 파일을 특정 폴더에 저장하는 로직입니다.

private fun runServer() {
disposable?.dispose()
disposable = Observable.just("")
.subscribeOn(Schedulers.io())
.map {
val serverSocket = ServerSocket(8988)
val client = serverSocket.accept()

val savePath = "${Environment.getExternalStorageDirectory()}/${activity?.packageName}/${System.currentTimeMillis()}.png"
val f = File(savePath)

val dirs = File(f.parent)
if (!dirs.exists())
dirs.mkdirs()
f.createNewFile()

val inputstream = client.getInputStream()
copyFile(inputstream, FileOutputStream(f))
serverSocket.close()
f
}
.doOnError {
it.printStackTrace()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
runServer()
textView.text = "${it.path} 로 파일을 저장하는데 성공하였습니다.\n${Calendar.getInstance().time}}"
}

}


PictureFragment.kt

파일을 송신하는 로직입니다.

host 의 주소는 wifip2pinfo 객체에서 groupOwnerAddress로 얻어올 수 있습니다.

private fun sendPictureFile(uri: Uri) {
val host = info.groupOwnerAddress
val socket = Socket()
val port = 8988

println("fileUri : $uri, host : $host, port : $port")

try {
socket.bind(null)
socket.connect(InetSocketAddress(host, port), 5000)

val outputStream = socket.getOutputStream()
val cr = activity?.contentResolver
var inputStream: InputStream? = null
try {
inputStream = cr?.openInputStream(uri)
} catch (e: FileNotFoundException) {
e.printStackTrace()
}


val buf = ByteArray(1024)

val total = File(uri.path).length()
var sum: Long = 0

try {
var len = inputStream!!.read(buf)
sum += len
while (len != -1) {
outputStream.write(buf, 0, len)
len = inputStream.read(buf)
sum += len
SendFilePercentEvent.send(total, sum)
println("copyFile len : $len")
}
outputStream.close()
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (socket != null) {
if (socket.isConnected) {
try {
socket.close()
} catch (e: IOException) {
// Give up
e.printStackTrace()
}

}
}
}
}


스크린샷

성의없는 스샷 (ㅇㅇ)




대략 제가 만든 예제는 위와같이 구성되어있습니다.

관련된 일을 진행하실때 누군가에겐 도움이 되길 바랍니다.





'개발 > ANDROID' 카테고리의 다른 글

비트맵을 겹쳐서 OOM 극복하기  (0) 2019.04.01
[트랜스퍼모드] xPermode 적용기  (0) 2019.02.11
안드로이드 투명 인디케이터 만들기  (0) 2018.10.17
fontFamily  (0) 2018.10.15
Android Flavors  (0) 2018.10.01
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크