回答編集履歴

3

コード追加

2021/10/09 12:49

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -17,3 +17,279 @@
17
17
  なお、参考とされているドキュメントは ORACLE のもので、つまり PC 用 Java のものです。
18
18
 
19
19
  基本同じとはいえ、[Android のドキュメント](https://developer.android.com/reference/java/net/Socket#setSoTimeout(int)) を参考にされたほうが良いと思います。
20
+
21
+
22
+
23
+ ----
24
+
25
+
26
+
27
+ ハンドラでコルーチンという構造が良く分からなかったので、 java から変換する形でハンドラだけのものを作ってみました。
28
+
29
+ ByteBuffer 使用部分も素直に配列にしています。
30
+
31
+
32
+
33
+ MainActivity.kt
34
+
35
+ ```kotlin
36
+
37
+ package com.teratail.a363500
38
+
39
+
40
+
41
+ import android.os.Bundle
42
+
43
+ import android.os.Handler
44
+
45
+ import android.os.HandlerThread
46
+
47
+ import android.util.Log
48
+
49
+ import android.widget.Button
50
+
51
+ import androidx.appcompat.app.AppCompatActivity
52
+
53
+ import java.io.IOException
54
+
55
+ import java.io.InputStream
56
+
57
+ import java.io.OutputStream
58
+
59
+ import java.net.InetSocketAddress
60
+
61
+ import java.net.Socket
62
+
63
+ import java.net.SocketAddress
64
+
65
+
66
+
67
+ class MainActivity : AppCompatActivity() {
68
+
69
+ private lateinit var ht: HandlerThread
70
+
71
+ private lateinit var pinger: Pinger
72
+
73
+
74
+
75
+ override fun onCreate(savedInstanceState: Bundle?) {
76
+
77
+ super.onCreate(savedInstanceState)
78
+
79
+ setContentView(R.layout.activity_main)
80
+
81
+
82
+
83
+ ht = HandlerThread("PingerThread")
84
+
85
+ ht.start()
86
+
87
+
88
+
89
+ val endpoint = InetSocketAddress("10.0.2.2", 2001) //エミュレータ → ホストPC
90
+
91
+ pinger = Pinger(endpoint, 5000, 500, 1000)
92
+
93
+
94
+
95
+ val button = findViewById<Button>(R.id.button) //ボタンを押すと開始
96
+
97
+ button.setOnClickListener { pinger.start(Handler(ht.looper)) }
98
+
99
+ }
100
+
101
+
102
+
103
+ override fun onDestroy() {
104
+
105
+ super.onDestroy()
106
+
107
+ ht.quit()
108
+
109
+ pinger.stop()
110
+
111
+ }
112
+
113
+ }
114
+
115
+
116
+
117
+ class Pinger(val endpoint: SocketAddress, val interval: Long, val timeoutOfConnect: Int, val timeoutOfRead: Int) {
118
+
119
+ companion object {
120
+
121
+ private const val TAG = "Pinger"
122
+
123
+ }
124
+
125
+
126
+
127
+ private var s: Socket? = null
128
+
129
+
130
+
131
+ fun start(handler: Handler) {
132
+
133
+ handler.post {
134
+
135
+ try {
136
+
137
+ s = Socket().also {
138
+
139
+ it.connect(endpoint, timeoutOfConnect)
140
+
141
+ it.soTimeout = timeoutOfRead
142
+
143
+ val output = it.getOutputStream()
144
+
145
+ val input = it.getInputStream()
146
+
147
+
148
+
149
+ if (ping(output, input)) next(handler, output, input)
150
+
151
+ }
152
+
153
+ } catch (e: Exception) {
154
+
155
+ e.printStackTrace()
156
+
157
+ }
158
+
159
+ }
160
+
161
+ }
162
+
163
+
164
+
165
+ private fun next(handler: Handler, output: OutputStream, input: InputStream) {
166
+
167
+ handler.postDelayed({
168
+
169
+ if (ping(output, input)) next(handler, output, input)
170
+
171
+ }, interval)
172
+
173
+ }
174
+
175
+
176
+
177
+ private fun ping(output: OutputStream, input: InputStream): Boolean {
178
+
179
+ try {
180
+
181
+ Log.d(TAG, "write")
182
+
183
+ output.write(byteArrayOf(1,2,3,4))
184
+
185
+
186
+
187
+ Log.d(TAG, "read")
188
+
189
+ val buf = ByteArray(4);
190
+
191
+ val len = input.read(buf)
192
+
193
+ Log.d(TAG, "len=$len")
194
+
195
+ if (len < buf.size) {
196
+
197
+ Log.d(TAG, "切断")
198
+
199
+ stop()
200
+
201
+ return false
202
+
203
+ }
204
+
205
+ if (buf.contentEquals(byteArrayOf(1,2,3,4))) {
206
+
207
+ Log.d(TAG, "OK")
208
+
209
+ } else {
210
+
211
+ Log.d(TAG, "data error");
212
+
213
+ }
214
+
215
+ } catch (e: Exception) {
216
+
217
+ e.printStackTrace()
218
+
219
+ stop()
220
+
221
+ return false
222
+
223
+ }
224
+
225
+ return true
226
+
227
+ }
228
+
229
+
230
+
231
+ fun stop() {
232
+
233
+ Log.d(TAG, "stop")
234
+
235
+ try {
236
+
237
+ s?.close()
238
+
239
+ } catch (ignore: IOException) {
240
+
241
+ }
242
+
243
+ s = null
244
+
245
+ }
246
+
247
+ }
248
+
249
+ ```
250
+
251
+ レイアウト: activity_main.xml
252
+
253
+ ```xml
254
+
255
+ <?xml version="1.0" encoding="utf-8"?>
256
+
257
+ <androidx.constraintlayout.widget.ConstraintLayout
258
+
259
+ xmlns:android="http://schemas.android.com/apk/res/android"
260
+
261
+ xmlns:app="http://schemas.android.com/apk/res-auto"
262
+
263
+ xmlns:tools="http://schemas.android.com/tools"
264
+
265
+ android:layout_width="match_parent"
266
+
267
+ android:layout_height="match_parent"
268
+
269
+ tools:context=".MainActivityJ">
270
+
271
+
272
+
273
+ <Button
274
+
275
+ android:id="@+id/button"
276
+
277
+ android:layout_width="wrap_content"
278
+
279
+ android:layout_height="wrap_content"
280
+
281
+ android:text="START"
282
+
283
+ app:layout_constraintBottom_toBottomOf="parent"
284
+
285
+ app:layout_constraintLeft_toLeftOf="parent"
286
+
287
+ app:layout_constraintRight_toRightOf="parent"
288
+
289
+ app:layout_constraintTop_toTopOf="parent" />
290
+
291
+
292
+
293
+ </androidx.constraintlayout.widget.ConstraintLayout>
294
+
295
+ ```

2

参考リンクについて追加

2021/10/09 12:48

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -11,3 +11,9 @@
11
11
 
12
12
 
13
13
  read でタイムアウトを発生させるのであれば、ソケットは繋いだままでサーバからの送信を遅らせる形でテストするべきと思います。
14
+
15
+
16
+
17
+ なお、参考とされているドキュメントは ORACLE のもので、つまり PC 用 Java のものです。
18
+
19
+ 基本同じとはいえ、[Android のドキュメント](https://developer.android.com/reference/java/net/Socket#setSoTimeout(int)) を参考にされたほうが良いと思います。

1

修正

2021/10/09 11:39

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  恐らく原因はそのテスト方法とコードの不備と思います。
6
6
 
7
- 「途中でサーバーを落とし、タイムアウト(もしくは切断)を検知できるか」と言われていますが、クライアントが read 中にサーバを落としソケットを切断した場合、タイムアウト(等の例外の発生)では無く read (のブロック) が戻るだけです。
7
+ 「途中でサーバーを落とし、タイムアウト(もしくは切断)を検知できるか」と言われていますが、クライアントが read 中にソケットを切断した場合、タイムアウト(等の例外の発生)では無く read (のブロック) が戻るだけです。
8
8
 
9
9
  必要なのはその時の read の戻り値から何が起きたのかを判断するのであり、ご提示のコードでは判断どころか戻り値の取得もしていません。
10
10