Android网络通信——使用Ktor创建服务器

1、概述

Ktor是一个开源的,使用Kotlin语言编写的网络库,可以做客户端,也可以做服务端

2、添加依赖

libs.versions.toml

[versions]
ktorServerCore = "1.6.7"
ktorServerNetty = "1.6.7"

[libraries]
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktorServerCore" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktorServerNetty" }}

3、获取IP地址

package com.example.ktorserverdemo

import android.content.Context
import android.content.res.AssetManager
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.net.Inet4Address
import java.net.NetworkInterface


val localIpv4Address: String?
    get() {
        val en = NetworkInterface.getNetworkInterfaces()
        while (en.hasMoreElements()) {
            val netInterface = en.nextElement()
            val enIpAddress = netInterface.inetAddresses
            while (enIpAddress.hasMoreElements()) {
                val inetAddress = enIpAddress.nextElement()
                if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
                    return inetAddress.hostAddress!!.toString()
                }
            }
        }
        return null
    }

fun getAssetFileAsFile(assetManager: AssetManager, fileName: String): File? {
    try {
        val inputStream = assetManager.open(fileName)
        val tempFile = File.createTempFile("temp", null)
        tempFile.deleteOnExit()
        val outputStream = FileOutputStream(tempFile)
        val buff = ByteArray(1024)
        var length: Int
        while (inputStream.read(buff).also { length = it } > 0) {
            outputStream.write(buff, 0, length)
        }
        outputStream.close()
        inputStream.close()
        return tempFile
    } catch (e: Exception) {
        e.printStackTrace()
        return null
    }
}


object AssetUtils {
    @Throws(IOException::class)
    fun getAssetFileAsFile(context: Context, fileName: String?): File {
        // 打开asset下的文件
        val assetManager = context.assets
        val inputStream = assetManager.open(fileName!!)

        // 将InputStream内容复制到临时文件中
        val tempFile = File.createTempFile("temp", null)
        tempFile.deleteOnExit() // 确保JVM退出时删除该临时文件
        val outputStream: OutputStream = FileOutputStream(tempFile)
        val buffer = ByteArray(1024)
        var length: Int
        while (inputStream.read(buffer).also { length = it } > 0) {
            outputStream.write(buffer, 0, length)
        }
        inputStream.close()
        outputStream.close()

        // 返回File对象
        return tempFile
    }
}

创建一个顶层属性,其getter方法获取ip地址

4、创建Ktor服务器

4.1 创建工具类

创建一个Ktor服务器,并创建三个页面,一个根页面,一个读取xml文件并返回的页面,一个读取json文件并返回的页面。由于apk中存入了一个xml文件和一个json文件,存入了assets中,所以需要AssetManager

package com.example.ktorserverdemo

import android.content.res.AssetManager
import java.io.File
import java.io.FileOutputStream
import java.net.Inet4Address
import java.net.NetworkInterface


val localIpv4Address: String?
    get() {
        val en = NetworkInterface.getNetworkInterfaces()
        while (en.hasMoreElements()) {
            val netInterface = en.nextElement()
            val enIpAddress = netInterface.inetAddresses
            while (enIpAddress.hasMoreElements()) {
                val inetAddress = enIpAddress.nextElement()
                if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
                    return inetAddress.hostAddress!!.toString()
                }
            }
        }
        return null
    }

fun getAssetFileAsFile(assetManager: AssetManager, fileName: String): File? {
    try {
        val inputStream = assetManager.open(fileName)
        val tempFile = File.createTempFile("temp", null)
        tempFile.deleteOnExit()
        val outputStream = FileOutputStream(tempFile)
        val buff = ByteArray(1024)
        var length: Int
        while (inputStream.read(buff).also { length = it } > 0) {
            outputStream.write(buff, 0, length)
        }
        outputStream.close()
        inputStream.close()
        return tempFile
    } catch (e: Exception) {
        e.printStackTrace()
        return null
    }
}

创建Utils文件,里面有一个顶层属性,提供getter方法,用于返回设备的IP地址。有一个顶层函数,用于从assets中读取文件到File对象中

4.2 创建Ktor服务器

package com.example.ktorserverdemo

import android.content.res.AssetManager
import android.os.Build
import android.util.Log
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.features.CORS
import io.ktor.features.CallLogging
import io.ktor.http.ContentType

import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.get
import io.ktor.routing.put
import io.ktor.routing.routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class KtorServer(val assetManager: AssetManager) {
    companion object {
        private var instance: KtorServer? = null

        fun getInstance(assetManager: AssetManager): KtorServer {
            if (instance == null) {
                instance = KtorServer(assetManager = assetManager)
            }
            return instance!!
        }
    }

    private val TAG = "KtorServer"
    var root_response = "手机型号 ${Build.MODEL} 运行正常\n"
    private val server by lazy {
        embeddedServer(Netty, 12345) {
            install(CallLogging)
            install(CORS) {
                anyHost()
                header(HttpHeaders.ContentType)
                method(HttpMethod.Options)
                method(HttpMethod.Put)
                method(HttpMethod.Patch)
                method(HttpMethod.Delete)
            }
            routing {
                get("/") {
                    call.respondText(root_response, ContentType.Text.Plain)
                }
                get("/get_data_xml") {
                    val xmlFile = getAssetFileAsFile(assetManager, "web/get_data.xml")

                    if (xmlFile == null) {
                        call.respond(HttpStatusCode.NotFound, "file not exist")
                    } else {
                        call.respondText(
                            xmlFile.readText(),
                            ContentType.Text.Xml,
                            HttpStatusCode.OK
                        )
                    }
                }
                get("/get_data_json") {
                    val jsonFile = getAssetFileAsFile(assetManager, "web/get_data.json")
                    if (jsonFile == null) {
                        call.respond(HttpStatusCode.NotFound, "file not exist")
                    } else {
                        call.respondText(
                            jsonFile.readText(),
                            ContentType.Text.Xml,
                            HttpStatusCode.OK
                        )
                    }
                }
                put("/") {
                    Log.d(TAG, "put in root")
                    val requestData = call.receive<String>()
                    Log.d(TAG, "requestData = $requestData")
                    root_response += requestData
                    root_response += "\n"
                    call.respond(HttpStatusCode.OK, "根页面更新成功\n")
                }
            }
        }
    }

    fun start() {
        CoroutineScope(Dispatchers.IO).launch { server.start(true) }
    }

    fun stop() {
        server.stop(1_000, 2_000)
    }
}

创建了三个页面,用于get时返回。创建一个put,用于put数据到根页面。

4.3 MainActivity

package com.example.ktorserverdemo

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import com.example.ktorserverdemo.ui.theme.KtorServerDemoTheme

class MainActivity : ComponentActivity() {
    var ktorServer:KtorServer? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val assetManager = assets
        ktorServer = KtorServer.getInstance(assetManager)
        ktorServer?.start()
        val ip = localIpv4Address ?: "0.0.0.0"
        setContent {
            KtorServerDemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Text(text = "请访问 http://$ip:12345")
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        ktorServer?.stop()
    }
}

5 访问服务器

在这里插入图片描述
局域网内浏览器访问该页面就能够访问服务器了,这里直接adb使用curl访问

coral:/ # curl http://10.53.31.244:12345
手机型号 AOSP on coral 运行正常
coral:/ # curl http://10.53.31.244:12345/get_data_xml
<apps>
    <app>
        <id>1</id>
        <name>Google Maps</name>
        <version>1.0</version>
    </app>
    <app>
        <id>2</id>
        <name>Chrome</name>
        <version>2.1</version>
    </app>
    <app>
        <id>3</id>
        <name>Google Play</name>
        <version>3.2</version>
    </app>
</apps>
coral:/ # curl http://10.53.31.244:12345/get_data_json
[{"id":"5","version":"5.5","name":"Clash of Clans"},
{"id":"6","version":"7.0","name":"Boom Beach"},
{"id":"7","version":"3.5","name":"Clash Royale"}]
coral:/ # curl -X PUT -d "test" http://10.53.31.244:12345
根页面更新成功
coral:/ # curl http://10.53.31.244:12345
手机型号 AOSP on coral 运行正常
test
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值