이번 글에서는 단말과 단말 간에 블루투스를 활용하여,
소켓 통신으로 데이터를 주고받는 간단한 예제를 다뤄보려고 합니다.
해당 예제는 bluetoothServer, bluetoothClient 각각 두 개로 나뉘어 집니다.
(단말 두 개가 필요합니다..!)
WiFi 소켓 통신 예제가 필요하시다면 아래 링크를 참조해 주세요
↗[안드로이드] WiFi 소켓 통신 예제 (Server-Client)
1. 예제 다운로드
https://github.com/bictoselfdev/bluetoothClient 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()
}
}
0 comments: