You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于双向通信Socket编程的同网Android设备服务端-客户端APP开发问询

实现同一网络下Android设备双向通信的方案指南

Hey there! Let's break down exactly how to build a bidirectional communication app between two Android devices on the same local network—one acting as the server, the other as the client. I've put together a practical, step-by-step guide that you can follow right away:

1. 核心技术选型

First up, let's pick the right tools for the job:

  • TCP Socket: This is your go-to for reliable local network communication. It's natively supported on Android, requires no extra dependencies, and enables real-time, two-way data transfer.
  • WifiManager: Use this to fetch the server device's local IP address, so the client can locate and connect to it.

2. 服务端实现步骤

第一步:声明必要权限

Add these permissions to your AndroidManifest.xml—they let your app access the network and Wi-Fi details:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Android 10+ 需要后台网络权限 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

第二步:服务端核心逻辑

Create a manager class to handle socket listening, client connections, and message sending/receiving. Run all network operations on a background thread to avoid blocking the main UI thread:

class ServerManager(private val onMessageReceived: (String) -> Unit) {
    private var serverSocket: ServerSocket? = null
    private var clientSocket: Socket? = null
    private var inputStream: BufferedReader? = null
    private var outputStream: PrintWriter? = null
    private val port = 8888 // 自定义端口,确保两台设备都能访问

    fun startServer() {
        Thread {
            try {
                serverSocket = ServerSocket(port)
                // 等待客户端连接请求
                clientSocket = serverSocket?.accept()
                // 初始化输入输出流
                inputStream = BufferedReader(InputStreamReader(clientSocket?.inputStream))
                outputStream = PrintWriter(clientSocket?.outputStream, true)

                // 持续监听客户端发来的消息
                var message: String?
                while (inputStream?.readLine().also { message = it } != null) {
                    message?.let {
                        // 切换回主线程更新UI
                        Handler(Looper.getMainLooper()).post {
                            onMessageReceived(it)
                        }
                    }
                }
            } catch (e: IOException) {
                e.printStackTrace()
            } finally {
                closeConnections()
            }
        }.start()
    }

    // 向客户端发送消息
    fun sendMessage(message: String) {
        outputStream?.println(message)
    }

    // 关闭所有连接资源
    private fun closeConnections() {
        inputStream?.close()
        outputStream?.close()
        clientSocket?.close()
        serverSocket?.close()
    }
}

第三步:获取服务端本地IP

Add this helper function to get the server's local network IP address (so the client knows where to connect):

fun getLocalIpAddress(context: Context): String? {
    val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
    val ipAddress = wifiManager.connectionInfo.ipAddress
    return String.format(
        "%d.%d.%d.%d",
        (ipAddress and 0xff),
        (ipAddress shr 8 and 0xff),
        (ipAddress shr 16 and 0xff),
        (ipAddress shr 24 and 0xff)
    )
}

3. 客户端实现步骤

Create a client manager class to handle connecting to the server and exchanging messages:

class ClientManager(private val serverIp: String, private val onMessageReceived: (String) -> Unit) {
    private var socket: Socket? = null
    private var inputStream: BufferedReader? = null
    private var outputStream: PrintWriter? = null
    private val port = 8888 // 和服务端端口保持一致

    fun connectToServer() {
        Thread {
            try {
                socket = Socket(serverIp, port)
                inputStream = BufferedReader(InputStreamReader(socket?.inputStream))
                outputStream = PrintWriter(socket?.outputStream, true)

                // 持续监听服务端发来的消息
                var message: String?
                while (inputStream?.readLine().also { message = it } != null) {
                    message?.let {
                        Handler(Looper.getMainLooper()).post {
                            onMessageReceived(it)
                        }
                    }
                }
            } catch (e: IOException) {
                e.printStackTrace()
            } finally {
                closeConnections()
            }
        }.start()
    }

    // 向服务端发送消息
    fun sendMessage(message: String) {
        outputStream?.println(message)
    }

    // 关闭所有连接资源
    private fun closeConnections() {
        inputStream?.close()
        outputStream?.close()
        socket?.close()
    }
}

4. 整合到UI层

In your server Activity, you can initialize the manager and tie it to UI elements (like buttons and text views):

class ServerActivity : AppCompatActivity() {
    private lateinit var serverManager: ServerManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_server)

        // 显示服务端本地IP
        val localIp = getLocalIpAddress(this)
        tv_ip.text = "服务端IP: $localIp"

        // 初始化服务端管理器,处理收到的消息
        serverManager = ServerManager { message ->
            tv_received_message.text = "收到客户端消息: $message"
        }

        // 启动服务端
        btn_start_server.setOnClickListener {
            serverManager.startServer()
            tv_status.text = "服务端已启动,等待客户端连接..."
        }

        // 发送消息到客户端
        btn_send.setOnClickListener {
            val message = et_message.text.toString()
            serverManager.sendMessage(message)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 销毁时关闭连接
        serverManager.closeConnections()
    }
}

For the client Activity, simply let the user input the server's IP address, then connect and send/receive messages using the ClientManager class—similar to the server setup.

5. 关键注意事项

  • Always use background threads: Network operations can't run on the main thread (Android will throw a NetworkOnMainThreadException), so keep all socket logic in separate threads.
  • Handle runtime permissions: On Android 6.0+, you need to request dangerous permissions like ACCESS_WIFI_STATE and INTERNET at runtime—don't skip this, or your app will crash.
  • Choose a unique port: Stick to a port that's not commonly used (like 8888 or 9999) to avoid conflicts with other apps on the network.
  • Add reconnection logic: If the connection drops (e.g., one device loses Wi-Fi), add automatic retry logic to keep the app robust.
  • Serialize complex data: If you need to send objects instead of plain text, use libraries like Gson or ProtoBuf to convert them to strings/byte streams for transmission.

内容的提问来源于stack exchange,提问作者Ravi Verma

火山引擎 最新活动