前提・実現したいこと
こちらのKotlinのコードを参考に、スマホアプリをクライアントとしてTCP通信を行うプログラムを作成しています。
socket接続とサーバーとの電文のやり取りはできたのですが、outputStreamで送信してから〇秒以内に返信がない場合、というタイムアウトを設定することができません。
socket.soTimeoutを用いればタイムアウトを設定できるそうなのですが、動作してないように見えます。(java.net.SocketTimeoutExceptionに引っ掛かりません)
どのようにすればAndroidのKotlinでTCP通信のリードタイムアウトを設定できるのでしょうか?
socket.soTimeoutを使えるようにするのでも、何かタイマーを設けるものでも、タイムアウトの判定ができればいいです。
該当のソースコード
whileで連続してサーバーに送信し続け、途中でサーバーを落とし、タイムアウト(もしくは切断)を検知できるか、という風にしています。
MainActivity
kotlin
1 2import android.os.Bundle 3import android.os.Handler 4import android.util.Log 5import android.widget.Button 6import androidx.appcompat.app.AppCompatActivity 7import kotlinx.coroutines.channels.Channel 8import java.nio.ByteBuffer 9 10 11const val MSG_CONNECTION_SUCCESS = 111 // 接続成功 12const val MSG_CONNECTION_FAILED = 222 // 接続失敗 13const val MSG_IOEXCEPTION = 333 // 例外発生 14 15class MainActivity : AppCompatActivity() { 16 private var tcpcom: ComTcpClient? = null 17 val ip = "10.0.2.2" 18 val port = "2001" 19 private val TAG = MainActivity::class.java.simpleName 20 val handler = Handler() 21 22 override fun onCreate(savedInstanceState: Bundle?) { 23 super.onCreate(savedInstanceState) 24 setContentView(R.layout.activity_main) 25 26 connect() 27 28 val runnable = object: Runnable{ 29 override fun run(){ 30 val sendArray = ByteBuffer.allocate(30) 31 sendArray.putInt(0x01020304) 32 val byteArray = ByteBuffer.allocate(30) 33 34 try { 35 tcpcom?.sendOrReceive { outputStream, inputStream -> 36 println("output") 37 outputStream.write(sendArray.array()) 38 try{ 39 println("input") 40 inputStream.read(byteArray.array()) 41 42 } catch(e: Exception){ 43 println("error") 44 } 45 } 46 } catch(e: Exception){ 47 Log.d(TAG,"Failed...") 48 } 49 handler.postDelayed(this,5000) 50 } 51 } 52 handler.post(runnable) 53 54 } 55 56 fun connect(){ 57 val channel = Channel<Int>() 58 if (!ip.isEmpty() && !port.isEmpty()) { 59 tcpcom = ComTcpClient(ip, port.toInt(), channel) 60 tcpcom?.connect() 61 } 62 } 63}
ComTCPClient
サーバー切断後もjava.net.SocketTimeoutExceptionにもIOExceptionにも引っ掛かりません……。なんならExceptionにも引っ掛かりません。
kotlin
1 2import android.util.Log 3import kotlinx.coroutines.Dispatchers 4import kotlinx.coroutines.GlobalScope 5import kotlinx.coroutines.channels.Channel 6import kotlinx.coroutines.launch 7import java.io.IOException 8import java.io.InputStream 9import java.io.OutputStream 10import java.net.Socket 11import java.net.UnknownHostException 12 13class ComTcpClient(val ip: String, val port: Int, val channel: Channel<Int>) { 14 private var socket: Socket? = null 15 private val TAG = ComTcpClient::class.java.simpleName 16 17 fun connect() { 18 GlobalScope.launch(Dispatchers.Default) { 19 Log.d(TAG, "接続開始...") 20 try { 21 socket = Socket(ip, port) 22 channel.send(MSG_CONNECTION_SUCCESS) 23 24 } catch (e: IOException) { 25 Log.e(TAG, "IOException", e) 26 channel.send(MSG_IOEXCEPTION) 27 28 } catch (e: UnknownHostException) { 29 Log.e(TAG, "UnknownHostException", e) 30 channel.send(MSG_CONNECTION_FAILED) 31 } 32 } 33 } 34 35 fun sendOrReceive(callback: (OutputStream, InputStream) -> Unit) { 36 socket?.soTimeout = 1000 37 if (socket == null) throw java.lang.IllegalStateException() 38 socket?.also { socket -> 39 GlobalScope.launch(Dispatchers.Default) { 40 try { 41 if (socket.isConnected) { 42 println("SOCKET!") 43 callback(socket.outputStream, socket.inputStream) 44 } else { 45 channel.send(MSG_CONNECTION_FAILED) 46 println("disconnect...") 47 } 48 } catch(e: java.net.SocketTimeoutException){ 49 println("Timeout!!") 50 } catch (e: IOException) { 51 channel.send(MSG_IOEXCEPTION) 52 println("socket error") 53 } 54 } 55 } 56 } 57 58 fun close() { 59 if (socket == null) throw java.lang.IllegalStateException() 60 socket?.also { socket -> 61 GlobalScope.launch(Dispatchers.Default) { 62 try { 63 if (socket.isConnected) socket.close() 64 } catch (e: IOException) { 65 channel.send(MSG_IOEXCEPTION) 66 } 67 } 68 } 69 } 70}
一応サーバー側も記載しておきます。両者はローカルループバックで接続しています。
python
1import socketserver 2 3class MyTCPHandler(socketserver.BaseRequestHandler): 4 def handle(self): 5 while(True): 6 self.data = self.request.recv(1024).strip() 7 print("{} wrote:".format(self.client_address[0])) 8 print(self.data) 9 10 send_array = (0x01020304).to_bytes(4,byteorder="big",signed="true") 11 12 self.request.sendall(send_array) 13 14if __name__ == "__main__": 15 HOST, PORT = '127.0.0.1', 2001 16 17 with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server: 18 server.serve_forever() 19
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/10/09 11:35 編集
2021/10/09 11:50 編集
2021/10/10 11:07