我想让我的 Android 应用程序成为服务器。我有一个 SocketServerManger 类,从“org.java-websocket:Java-WebSocket:1.5.6”库实现 WebSocketServer。
当我使用 ws 连接连接到我的服务器时,它可以正常工作。问题是当我的客户端想要使用 wss 连接来连接它时。 这是我的代码:
class SocketServerManger(
port: Int,
val socketListener: List<WebSocketConnectionListener>,
context: Context):WebSocketServer(InetSocketAddress(port)) {
var connection: WebSocket? = null
private val _isConnectionOpen = MutableStateFlow(false)
val isConnectionOpen: StateFlow<Boolean> = _isConnectionOpen
/*this part is added to set ssl context . without this part server works correctly with ws connection*/
init {
createSSLContext(context)?.let {
setWebSocketFactory(DefaultSSLWebSocketServerFactory(it))
}
}
private fun createSSLContext(context: Context): SSLContext? {
try {
val keystoreFileName = "keystore.bks"
val keystorePassword = "password"
val keystoreInputStream: InputStream = context.assets.open(keystoreFileName)
val keyStore = KeyStore.getInstance("BKS")
keyStore.load(keystoreInputStream, keystorePassword.toCharArray())
keystoreInputStream.close()
val keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(keyStore, keystorePassword.toCharArray())
val tmf : TrustManagerFactory = TrustManagerFactory.getInstance("BKS");
tmf.init(keyStore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, tmf.trustManagers, SecureRandom())
return sslContext
} catch (e: Exception) {
serverLog("createSSLContext ${e.message}")
}
return null
}
/*end of setup ssl*/
override fun onStart() {
serverLog("SocketServerManger onStart")
println("WebSocket server started")
}
override fun onOpen(conn: WebSocket?, handshake: ClientHandshake?) {
connection = conn
socketListener.forEach { it.onConnected() }
}
override fun onClose(conn: WebSocket?, code: Int, reason: String?, remote: Boolean) {
socketListener.forEach { it.onDisconnected(code, reason) }
}
override fun onMessage(conn: WebSocket?, message: String?) {
socketListener.forEach { it.onMessage(conn, message) }
}
override fun onError(conn: WebSocket?, ex: Exception?) {
socketListener.forEach { it.onError(ex) }
ex?.printStackTrace()
}
suspend fun sendMessagesUntilSuccess(timeoutMillis: Long = 50000, message: String): Boolean {
var success = false
while (!success) {
success = sendMessageWithTimeout(timeoutMillis, message)
if (!success) {
// Add a delay before retrying to avoid continuous retries and potential rate limiting
delay(1000)
}
}
return success
}
suspend fun sendMessageWithTimeout(timeoutMillis: Long = 5000, message: String): Boolean {
return withContext(Dispatchers.IO) {
return@withContext try {
withTimeout(timeoutMillis) {
val result = CoroutineScope(Dispatchers.IO).async {
try {
if (connection != null && connection!!.isOpen) {
connection!!.send(message)
true
} else {
false
}
} catch (e: org.java_websocket.exceptions.WebsocketNotConnectedException) {
socketListener.forEach { it.onException(e) }
false
}
}.await()
result
}
} catch (e: TimeoutCancellationException) {
socketListener.forEach { it.onException(e) }
false
} catch (e: Exception) {
socketListener.forEach { it.onException(e) }
false
}
}
}
fun isConnectionOpen() = connection?.isOpen ?: false
fun isPortAvailable(port: Int): Boolean {
return try {
ServerSocket(port).close()
true
} catch (e: IOException) {
false
}
}
}
当我想使用 Web 客户端连接到服务器时,连接超时。
使用自签名证书在 Android 上设置 ssl 需要一些步骤,应正确遵循这些步骤,否则您会遇到一些问题。
1/安装OpenSSL
2/生成私钥
使用OpenSSL生成私钥
openssl genrsa -out key.pem 2048
3/ 创建证书签名请求 (CSR) 使用私钥创建 CSR
openssl req -new -key key.pem -out csr.pem
4/ 生成自签名证书 您可以使用私钥和 CSR 生成自签名证书。
openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out certificate.pem
5/ 将证书转换为适当的格式
android支持bks。首先,您应该将证书转换为 PKCS#12 格式,扩展名为 .p12 或 .pfx,然后转换为 bks 。
openssl pkcs12 -export -out certificate.p12 -inkey key.pem -in certificate.pem -name "alias"
keytool -importkeystore -srckeystore certificate.p12 -srcstoretype PKCS12 -destkeystore certificate.bks -deststoretype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "D:\bcprov-debug-jdk18on-1.78.1.jar
6/ 将bks文件格式放入你的android项目中。我把它放在我的资产中。
7/ 在代码中设置 ssl :
` 初始化 {
createSSLContext(context)?.let {
setWebSocketFactory(DefaultSSLWebSocketServerFactory(it))
}
}
private fun createSSLContext(context: Context): SSLContext? {
val keystoreFileName = "certificate.bks"
val keystorePassword = "yourpassword"
val keystoreInputStream: InputStream = context.assets.open(keystoreFileName)
try {
val keystore = KeyStore.getInstance("BKS")
keystore.load(keystoreInputStream, keystorePassword.toCharArray())
val keyManagerFactory = KeyManagerFactory.getInstance("X509")
keyManagerFactory.init(keystore, keystorePassword.toCharArray())
val tmf : TrustManagerFactory = TrustManagerFactory.getInstance("X509");
tmf.init(keystore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.keyManagers,tmf.trustManagers, null)
return sslContext
} catch (e: Exception) {
keystoreInputStream.close()
}
return null
}`
按照这些步骤最终实现与 wss 的连接。