본문 바로가기
안드로이드/코틀린

안드로이드 USB 소켓 통신 (예제 다운로드)

by 시작이반의반 2024. 11. 19.

안드로이드 USB 소켓 통신
안드로이드 USB 소켓 통신

안드로이드 개발 환경에서 다양한 장비와 기기를 네트워크로 연결해 데이터를 주고받는 기능은 필수적입니다.

네트워크 통신에 관심이 많은 저는 이러한 다양한 통신 방식을

하나의 모듈로 통합하고 관리할 수 있는 Connector 모듈을 작성하였습니다.

 

이번 글에서는 Connector 모듈을 활용해 안드로이드 USB 소켓 통신을 중점적으로 다룰 예정입니다.

예제의 주요 코드를 살펴보고, 안드로이드 단말과 PC 간에 데이터를 주고받는 테스트 방법도 확인할 수 있습니다.

또한 예제 코드는 GitHub에서 다운로드하여 직접 확인할 수 있습니다.

 

 

 

 

Connector 모듈 소개

 

Connector 모듈
Connector 모듈

Connector 모듈은 안드로이드에서 다양한 통신 방식을 하나의 구조로 통합하여 관리할 수 있도록 설계된 모듈입니다.

이 모듈을 통해 WiFi, USB, 블루투스, 시리얼 통신 등의 통신을 구현하고 효율적으로 유지 관리할 수 있으며

통신 방식에 관계없이 일관된 인터페이스를 제공합니다.

 

 

USB 소켓 통신

 

안드로이드 USB 소켓 통신
안드로이드 USB 소켓 통신

 

USB 소켓통신을 간략하게 아래와 같이 이해하면 좋을 것 같습니다.

  • 하나의 네트워크(USB 포트포워딩) 환경을 만든다.
  • 서버 측에서 Port를 열고 기다린다.
  • 클라이언트 측에서 서버 측의 IP, Port를 통해 연결한다.
  • 서로 데이터를 송수신한다.

 

포트포워딩이란?

데이터를 보낼 포트를 정하고, 그 포트를 통해 데이터가 어디로 이동할지 길을 만들어주는 과정입니다.

통신할 기기간에 usb를 연결하고 아래 명령어를 상황에 맞는 걸로 입력해 주세요.

adb forward tcp:5555 tcp:5555
* 예시: 호스트(PC)에서 디바이스(안드로이드)로 연결 요청을 해야 할 때

 

adb reverse tcp:5555 tcp:5555
* 예시: 디바이스(안드로이드)에서 호스트(PC)로 연결 요청을 해야 할 때

 

 

 

USB 소켓 통신 예제

 

USB 소켓 통신 예제에서 주요 코드를 살펴보려고 합니다.

해당 예제는 클라이언트 측 코드입니다.

 

1. ConnectionUSB 클래스

USB 소켓 통신의 실제 연결 및 데이터 송수신을 처리하는 역할을 하는 클래스입니다.

class ConnectionUSB(private val port: Int) : ConnectionHandler {
    private var socket: Socket? = null
    private var outputStream: OutputStream? = null
    private var inputStream: InputStream? = null

    override suspend fun connect(): Boolean = withContext(Dispatchers.IO) {
        try {
            val socketAddress = InetSocketAddress("127.0.0.1", port)
            socket = Socket()
            socket?.connect(socketAddress, 3000)
            outputStream = socket?.getOutputStream()
            inputStream = socket?.getInputStream()

            Log.d("Connector(USB)", "Connected to 127.0.0.1:$port")
            true
        } catch (e: IOException) {
            e.printStackTrace()
            false
        }
    }

    override fun disconnect() {
        try {
            outputStream?.close()
            inputStream?.close()
            socket?.close()

            Log.d("Connector(USB)", "Disconnected")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    override suspend fun sendData(data: ByteArray): Boolean = withContext(Dispatchers.IO) {
        try {
            outputStream?.write(data)
            outputStream?.flush()
            Log.d("Connector(USB)", "Data sent: ${String(data)}")
            true
        } catch (e: IOException) {
            e.printStackTrace()
            false
        }
    }

    override suspend fun receiveData(): ByteArray? = withContext(Dispatchers.IO) {
        try {
            val buffer = ByteArray(ConnectionUtil.BUFFER_SIZE)
            withTimeout(ConnectionUtil.TIMEOUT.toLong()) {
                var receivedData: ByteArray? = null
                val bytesRead = inputStream?.read(buffer) ?: -1
                if (bytesRead > 0) {
                    receivedData = buffer.copyOf(bytesRead)

                    Log.d("Connector(USB)", "Data received: ${String(receivedData).trim()}")
                }
                receivedData
            }
        } catch (e: IOException) {
            e.printStackTrace()
            null
        }
    }
}

 

 

2. ControllerUSB 클래스

ConnectionUSB 기능을 관리하고 제어하는 상위 컨트롤러 클래스입니다.

class ControllerUSB : BaseController() {
    private var connectionUSB: ConnectionUSB? = null

    suspend fun connectTo(port: Int): Boolean {
        connectionUSB = ConnectionUSB(port)
        return startConnection()
    }

    override suspend fun connect(): Boolean {
        return (connectionUSB?.connect() ?: false)
            .also { isConnected ->
                if (isConnected) connectionType = ConnectionUtil.ConnectionType.USB
            }
    }

    override fun disconnect() {
        connectionUSB?.disconnect()
        connectionType = ConnectionUtil.ConnectionType.NONE
    }

    override suspend fun sendData(data: ByteArray): Boolean {
        return connectionUSB?.sendData(data) ?: false
    }

    override suspend fun receiveData(): ByteArray? {
        return connectionUSB?.receiveData()
    }
}

 

 

3. MainActivity 동작 구현

ControllerUSB를 통해서 USB 소켓 연결동작, USB 소켓 송신 동작, USB 소켓 수신 동작을 각각 구현할 수 있습니다.

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private val controllerUSB = ControllerUSB()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.btnUsbConnect.setOnClickListener {
            // USB 소켓 연결 동작
            tryConnectToUSB()
        }

        binding.btnDisconnect.setOnClickListener {
            disconnectAll()
        }

        binding.btnSend.setOnClickListener {
            // USB 소켓 송신 동작
            val message = binding.etMessage.text.toString()
            sendMessageToTarget(message)
        }
    }

    private fun tryConnectToUSB() {
        try {
            CoroutineScope(Dispatchers.IO).launch {
                val port = binding.etUsbPort.text.toString().toInt()

                if (controllerUSB.connectTo(port)) {
                    updateLogView("[USB] Connected!\n")
                    
                    // USB 소켓 수신 동작
                    startReceivingData(controllerUSB)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun startReceivingData(controller: BaseController) {
        CoroutineScope(Dispatchers.IO).launch {
            while (controller.connectionType != ConnectionType.NONE) {
                val receivedData = controller.receiveData()
                receivedData?.let {
                    val receivedMessage = String(it).trim()
                    updateLogView("Received: $receivedMessage\n")
                }
                delay(500)
            }
        }
    }

    private fun disconnectAll() {
        //controllerWiFi.terminateConnection()
        controllerUSB.terminateConnection()
        //controllerBT.terminateConnection()
        //controllerSerial.terminateConnection()

        updateLogView("Disconnected All!\n")
    }

    private fun sendMessageToTarget(message: String) {
        try {
            CoroutineScope(Dispatchers.IO).launch {
                when {
                    controllerUSB.connectionType == ConnectionType.USB -> {
                        if (controllerUSB.sendData(message.toByteArray())) {
                            updateLogView("Sent : $message\n")
                        }
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun updateLogView(message: String) {
        CoroutineScope(Dispatchers.Main).launch {
            binding.tvLog.text = "${binding.tvLog.text}$message"
        }
    }
}

 

 

 

 

USB 소켓 통신 예제 다운로드

 

안드로이드 USB 소켓 통신 예제
안드로이드 USB 소켓 통신 예제 실행 화면

 

 

 

 

 

USB 소켓 통신 테스트: 서버(PC) - 클라이언트(안드로이드)

 

테스트 방법은 안드로이드 단말과 PC 간에 USB 소켓 통신이 정상 동작하는지 확인해보려고 합니다.

클라이언트 역할로는 예제를 안드로이드 단말에 실행시켜 주고,

서버 역할로는 PC에서 Hercules(네트워크 및 시리얼 통신 테스트) 프로그램을 실행시켜 줍니다.

 

 

 

먼저 포트 포워딩을 통해서 USB 소켓 통신 환경을 만들어 줍니다.

 

1. 포트 포워딩

 - PC와 안드로이드 단말을 USB 연결

 - PC cmd 창을 열어서 명령어 입력 (adb )

안드로이드 USB 소켓 통신 포트포워딩
USB 소켓 통신 포트 포워딩

 

포트 포워딩으로 하나의 네트워크 환경이 만들어졌다면 아래와 같이 진행합니다.

 

1. 서버(PC Hercules 프로그램)

 - TCP Server 카테고리 이동

 - 특정 Port를 입력하고 Listen 버튼 클릭

 - 연결이 됐다면 Send Message 입력하고 Send 버튼 클릭

 

2. 클라이언트(안드로이드 예제)

 - 서버 측에 포트가 열렸다면 IP, Port로 연결

 - 연결이 됐다면 Send Message 입력하고 보내기 버튼 클릭

 

테스트 결과는 아래와 같습니다.

USB 소켓 통신 서버(PC)USB 소켓 통신 클라이언트(안드로이드)
USB 소켓 통신: 서버(PC) - 클라이언트(안드로이드)

 

 

 

끝으로..

 

WiFi 소켓통신에 이어서 USB 소켓 통신에 대해서 알아보았습니다. 같은 TCP 소켓 통신을 가지기 때문에 코드상에 큰 차이는 없었지만 각각의 네트워크 환경을 만드는 과정에서 차이점을 확인할 수 있었습니다. C-Type USB 케이블만 있다면 쉽게 USB 통신이 가능하다는 걸 직접 예제를 작성하면서 많은 공부가 되었던 것 같습니다. 

 

좋은 하루 보내세요:)

 

 

관련 글

안드로이드 WiFi 소켓 통신

 

안드로이드 WiFi 소켓 통신 (예제 다운로드)

안드로이드 개발 환경에서 다양한 장비와 기기를 네트워크로 연결해 데이터를 주고받는 기능은 필수적입니다.네트워크 통신에 관심이 많은 저는 이러한 다양한 통신 방식을하나의 모듈로 통합

joo-selfdev.tistory.com

안드로이드 블루투스 소켓 통신

 

안드로이드 블루투스 소켓 통신 예제 다운로드

안드로이드 개발 환경에서 다양한 장비와 기기를 네트워크로 연결해 데이터를 주고받는 기능은 필수적입니다.네트워크 통신에 관심이 많은 저는 이러한 다양한 통신 방식을하나의 모듈로 통합

joo-selfdev.tistory.com

안드로이드 UART 시리얼 통신

 

안드로이드 UART 시리얼 통신 예제 다운로드

시리얼 통신은 무선 네트워크 환경에 영향을 받지 않고안정성과 보안성을 유지할 수 있어 안드로이드 개발에서 많이 사용됩니다.이번 글에서는 안드로이드 UART 시리얼 통신을 중심으로 다뤄보

joo-selfdev.tistory.com

댓글