이번 글에서는 단말과 단말 간에 블루투스를 활용하여, 소켓 통신으로 데이터를 주고받는 간단한 예제를 다뤄보려고 합니다. 해당 예제는 bluetoothServer, bluetoothClient 각각 두 개로 나뉘어 집니다.  (단말 두 개가 필...

[안드로이드] Bluetooth 소켓 통신 예제 (Server-Client)

 

 

이번 글에서는 단말과 단말 간에 블루투스를 활용하여,

소켓 통신으로 데이터를 주고받는 간단한 예제를 다뤄보려고 합니다.

해당 예제는 bluetoothServer, bluetoothClient 각각 두 개로 나뉘어 집니다. 

(단말 두 개가 필요합니다..!)


WiFi 소켓 통신 예제가 필요하시다면 아래 링크를 참조해 주세요

↗[안드로이드] WiFi 소켓 통신 예제 (Server-Client)




1. 예제 다운로드

https://github.com/bictoselfdev/bluetoothClient 

https://github.com/bictoselfdev/bluetoothServer




2. 블루투스 소켓 통신 순서도

 

 

 - 블루투스 기반 소켓 통신을 다루기 위해서 페어링된 장비 정보를 준비합니다. (BluetoothDevice)

 - 클라이언트에서 페어링된 정보를 통해 소켓 생성 및 연결을 시도합니다.

 - 서버에서는 accept() 를 통해 소켓을 반환 받을 수 있습니다.

 - 서버와 클라이언트는 서로 read() / wirte() 를 통해 데이터를 송수신 할 수 있습니다.




3. 블루투스 장비 정보 가져오기

블루투스를 활용하기 위해 아래 권한을 manifests 에 추가해 줍니다.

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />


로컬 장치 블루투스 adpater 를 아래와 같이 가져올 수 있는데, 

이를 통해 기본적인 블루투스 작업이 가능합니다. 

지금 저에게 필요한 페어링된 장비 정보를 가져올 때도 사용됩니다.

private var btAdapter = BluetoothAdapter.getDefaultAdapter()


아래와 같이 페어링된 장비 List 를 가져올 수 있습니다.

그리고 List 중에서 자신이 연결하고자 하는 BluetoothDevice 를 가져 올 수 있어야합니다.

fun getPairedDevices(): ArrayList<BluetoothDevice> {
val deviceList = ArrayList<BluetoothDevice>()
val pairedDevices = btAdapter.bondedDevices
if (pairedDevices.size > 0) {
for (device in pairedDevices) {
deviceList.add(device)
}
}
return deviceList
}




4. 클라이언트 소켓 생성 및 연결

ClientThread 는 소켓을 생성 및 연결을 시도하고 연결이 정상적으로 되었다면 데이터 송수신 준비를 합니다.

inner class ClientThread(device: BluetoothDevice) : Thread() {
private var socket: BluetoothSocket? = null

override fun run() {
btAdapter.cancelDiscovery()
try {
onLogPrint("Try to connect to server..")

socket?.connect() // 소켓 연결
} catch (e: Exception) {
onError(e)

e.printStackTrace()
disconnectFromServer()
}

if (socket != null) {
onConnect()

commThread = CommThread(socket)
commThread?.start()
}
}

fun stopThread() {
try {
socket?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}

init {
try {
// 소켓 생성
socket = device.createRfcommSocketToServiceRecord(BTConstant.BLUETOOTH_UUID_INSECURE)
} catch (e: Exception) {
e.printStackTrace()
}
}
}




5. 서버 Accept 및 소켓 반환

AcceptThread 는 Client 로 부터 연결 요청을 기다립니다. 요청이 들어오면 accept() 로부터 소켓을 반환을 수 있습니다.

정상적으로 소켓을 반환 받았다면 데이터 송수신 할 준비를 합니다.

inner class AcceptThread : Thread() {
private var acceptSocket: BluetoothServerSocket? = null
private var socket: BluetoothSocket? = null

override fun run() {
while (true) {
socket = try {
acceptSocket?.accept() // 연결 요청이 오면 소켓 반환
} catch (e: Exception) {
e.printStackTrace()
break
}

if (socket != null) {
onConnect()

commThread = CommThread(socket)
commThread?.start()
break
}
}
}

fun stopThread() {
try {
acceptSocket?.close()
socket?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}

init {
try {
// 소켓 생성 For Accept
acceptSocket = btAdapter.listenUsingRfcommWithServiceRecord(
"bluetoothTest",
BTConstant.BLUETOOTH_UUID_INSECURE)
} catch (e: Exception) {
e.printStackTrace()
}
}
}




6. 데이터 송수신

데이터 송수신은 입출력 스트림을 활용합니다. (inputstream / outputstream)

CommThread 는 Server / Client 가 각각 데이터를 수신할 때 사용됩니다. (read)

sendData 메서드는 데이터를 송신할 때 사용됩니다. (write)

internal inner class CommThread(private val socket: BluetoothSocket?): Thread() {

override fun run() {
try {
outputStream = socket?.outputStream
inputStream = socket?.inputStream
} catch (e: Exception) {
e.printStackTrace()
}

var len: Int
val buffer = ByteArray(1024)
val byteArrayOutputStream = ByteArrayOutputStream()

while (true) {
try {
// 데이터 수신
len = socket?.inputStream?.read(buffer)!!
val data = buffer.copyOf(len)
byteArrayOutputStream.write(data)

socket.inputStream?.available()?.let { available ->

if (available == 0) {
val dataByteArray = byteArrayOutputStream.toByteArray()
val dataString = String(dataByteArray)
onReceive(dataString)

byteArrayOutputStream.reset()
}
}
} catch (e: Exception) {
e.printStackTrace()
stopThread()
accept()
break
}
}
}

fun stopThread() {
try {
inputStream?.close()
outputStream?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}

fun sendData(msg: String) {
if (outputStream == null) return

try {
// 데이터 송신
outputStream?.let {
onSend(msg)

it.write(msg.toByteArray())
it.flush()
}
} catch (e: Exception) {
onError(e)
e.printStackTrace()
stop()
}
}





6. 결과


            

0 comments: