Android で WebSocketサーバー 【Kotlin/アプリ開発】
Android WebSocketサーバー (Kotlin)
AndroidでWebSocketサーバーを立ち上げ、macOSのPythonクライアントからテキストを受信する仕組みを作ってみました。
WebSocketサーバーの実装には Java-WebSocket
ライブラリを使用できます。
build.gradle
に依存関係を追加
./app/build.gradle.kts
の
dependencies
に以下を追加します。
implementation("org.java-websocket:Java-WebSocket:1.5.2")
WebSocketサーバークラス (Kotlin)
WebSocketServer
クラスを継承させて、次のようなサーバークラスを用意します。メッセージを受信したらActivityで処理させるように、コールバックインタフェースも実装してます。
package com.example.protowebsocket
import org.java_websocket.WebSocket
import org.java_websocket.handshake.ClientHandshake
import org.java_websocket.server.WebSocketServer
import java.net.InetSocketAddress
class MyWebSocketServer(address: InetSocketAddress) : WebSocketServer(address) {
// コールバックインターフェースの定義
interface WebSocketCallback {
fun onMessageReceived(message: String)
}
// コールバックのインスタンス
private var callback: WebSocketCallback? = null
// コールバックを設定するメソッド
fun setWebSocketCallback(callback: WebSocketCallback?) {
this.callback = callback
}
override fun onOpen(conn: WebSocket, handshake: ClientHandshake) {
("New connection: ${conn.remoteSocketAddress}")
println}
override fun onClose(conn: WebSocket, code: Int, reason: String, remote: Boolean) {
("Connection closed: ${conn.remoteSocketAddress}")
println}
override fun onMessage(conn: WebSocket, message: String) {
("Received: $message")
println// コールバックでActivityにメッセージを通知
?.onMessageReceived(message)
callback}
override fun onError(conn: WebSocket?, ex: Exception) {
("Error: ${ex.message}")
println}
override fun onStart() {
("WebSocket server started on port: $port")
println}
}
ボタンやテキストViewなどのレイアウトは、適宜合わせて用意してください。
WebSocketサーバーの起動
先ほどのクラスを Activity で呼び出し、サーバーを起動させます。
package com.example.protowebsocket
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.net.InetSocketAddress
import java.net.NetworkInterface
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
private var server: MyWebSocketServer? = null
private lateinit var messageTextView: TextView
private var isWsServerStarted = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
()
enableEdgeToEdge(R.layout.activity_main)
setContentView= findViewById(R.id.messageTextView)
messageTextView
.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
ViewCompatval systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
v
insets}
val wsServerButton: Button = findViewById(R.id.wsServerButton)
.setOnClickListener {
wsServerButtonif (!isWsServerStarted) {
()
startWsServer.text = "Stop"
wsServerButton= true
isWsServerStarted .text = "メッセージの受信待ち。"
messageTextView} else {
()
stopWsServer.text = "Start"
wsServerButton= false
isWsServerStarted .text = ""
messageTextView}
}
}
// Activityが破棄される際にリソースを解放
override fun onDestroy() {
super.onDestroy()
("onDestory()が呼ばれた")
println
()
stopWsServer
}
private fun startWsServer() {
// 非同期でIPアドレスを取得してWebSocketサーバーを起動
(Dispatchers.Main).launch {
CoroutineScopeval ipAddress = getIPAddress()
("IP Address: $ipAddress")
println
// WebSocketサーバーを起動
= MyWebSocketServer(InetSocketAddress(ipAddress, 8765))
server // WebSocketサーバーのコールバックを設定
?.setWebSocketCallback(object : MyWebSocketServer.WebSocketCallback {
serveroverride fun onMessageReceived(message: String) {
// UIスレッドでTextViewを更新
{
runOnUiThread .text = message
messageTextView}
}
})
("Begin server... ws://${ipAddress}:8765")
println
{
thread // TODO 「Error: Address already in use」 だった場合にstopさせてからもう一度処理する
?.start()
server}
}
}
private fun stopWsServer() {
// サーバーが起動している場合、停止
?.stop()
server// コールバックの解放
?.setWebSocketCallback(null)
server("WebSocket server stopped.")
println}
// TODO serviceクラスへ移す
private fun getIPAddress(): String {
try {
val interfaces = NetworkInterface.getNetworkInterfaces()
for (iface in interfaces) {
("iface.name: ")
print(iface.name)
println// Wi-Fiインターフェースかどうか確認 (通常 "wlan0" がWi-Fiのインターフェース名)
if (iface.name.equals("wlan2", ignoreCase = true) && iface.isUp) {
val addresses = iface.inetAddresses
for (addr in addresses) {
if (!addr.isLoopbackAddress && addr.hostAddress.indexOf(':') == -1) {
return addr.hostAddress ?: "127.0.0.1"
}
}
}
}
} catch (e: Exception) {
.printStackTrace()
e}
return "127.0.0.1" // IPが取得できなかった場合のフォールバック
}
}
インターネットの使用を許可する
AndroidManifest.xml
に以下を追加して、インターネットの使用を許可します。
uses-permission android:name="android.permission.INTERNET" /> <
Pythonクライアント
AndroidアプリへWebSocketでメッセージを送信するために、macOSなどにPythonクライアントを用意します。PythonのWebSocketクライアントには
websocket-client
ライブラリを使用します。
ライブラリをインストール
pip install websockets
Pythonクライアントコード
import asyncio
import websockets
async def send_message():
= "ws://192.168.27.211:8765" # 環境に合わせてアドレスを変更してください
uri async with websockets.connect(uri) as websocket:
= "こんにちは、Websocket通信でメッセージを送ってます。"
message await websocket.send(message)
print(f"Sent message: {message}")
= await websocket.recv()
response print(f"Received message: {response}")
asyncio.get_event_loop().run_until_complete(send_message())
これで、Androidデバイス上のWebSocketサーバーが起動し、macOSのPythonクライアントからテキストメッセージを送信して受信できるはずです。
作成したAndroidプロジェクトは、GitHubで公開中です。 こちらのページ のTryWebSocketServerをご参考ください。
関連記事
- Raspberry PiでWebSocket|Lチカ、WebSocketの理解
- MavenでJavaプロジェクトをビルドするまで【macOS】
- Java開発が爆速に!超便利なJShellの使い方
- たった数行で作るWebサーバー【Python x HTTPServer】
- PythonでSQLite3のベンチマークテスト