1 package net.osdn.gokigen.pkremote.camera.vendor.visionkids.wrapper.playback
3 import android.util.Log
4 import net.osdn.gokigen.pkremote.camera.utils.SimpleLogDumper
5 import java.io.ByteArrayOutputStream
6 import java.io.DataOutputStream
7 import java.io.InputStream
8 import java.lang.Exception
9 import java.net.InetSocketAddress
10 import java.net.Socket
11 import java.util.ArrayDeque
12 import java.util.Queue
14 class MyFtpClient(private val callbackReceiver: IFtpServiceCallback, private val isDumpReceiveLog: Boolean = false)
16 private var isStart = false
17 private var isConnected = false
18 private var socket: Socket? = null
19 private var dataOutputStream: DataOutputStream? = null
20 private var connectedAddress : String? = null
21 private val commandQueue : Queue<FtpCommand> = ArrayDeque()
23 private var isStartDataPort = false
24 private var isConnectedDataPort = false
25 private var socketDataPort: Socket? = null
26 private var dataOutputStreamDataPort: DataOutputStream? = null
28 fun connect(address: String)
32 connectedAddress = address
33 Log.v(TAG, "connect to $address")
38 Log.v(TAG, " connect() : $address")
40 socket?.reuseAddress = true
41 socket?.keepAlive = true
42 socket?.tcpNoDelay = true
45 socket?.keepAlive = false
46 socket?.setPerformancePreferences(0, 2, 0)
47 socket?.oobInline = true
48 socket?.reuseAddress = false
49 socket?.trafficClass = 0x80
51 socket?.connect(InetSocketAddress(address, FTP_CONTROL_PORT), 0)
52 dataOutputStream = DataOutputStream(socket?.getOutputStream())
55 // 接続後の一発目は、自動で読み込んでみる
56 val connectCommand = FtpCommand("connect", "connect")
57 receiveFromDevice(connectCommand, socket, DATA_POLL_QUEUE_MS, MAX_RETRY_WAIT_COUNT)
63 callbackReceiver.onReceivedFtpResponse("connect", -1, e.message?:"EXCEPTION")
74 fun enqueueCommand(command: FtpCommand): Boolean
78 Log.v(TAG, " Command Enqueue : ${command.command} : ${command.value}")
79 return commandQueue.offer(command)
90 Log.v(TAG, " ----- DISCONNECT -----")
97 isStartDataPort = false
99 isConnectedDataPort = false
101 connectedAddress = null
110 fun decidePassivePort(response: String)
114 // データポートの IPアドレスとポート番号を応答データから切り出す
115 val pickupString = response.substring((response.indexOf("(") + 1), response.indexOf(")"))
116 val dataStringArray = pickupString.split(",")
117 val dataPortAddress = dataStringArray[0] + "." + dataStringArray[1] + "." + dataStringArray[2] + "." + dataStringArray[3]
118 val dataPort = dataStringArray[4].toInt() * 256 + dataStringArray[5].toInt()
119 val passiveAddress = "$dataPortAddress:$dataPort:\r\n"
120 Log.v(TAG, " - - - - - - data Port : $passiveAddress ($pickupString ${dataStringArray.size})")
121 callbackReceiver.onReceivedFtpResponse("data_port", 0, passiveAddress)
127 sleep(RECEIVE_WAIT_MS)
130 fun openPassivePort(address: String): Boolean
135 // データポートをオープンして受信できるようにする
136 val accessPoint = address.split(":")
137 Log.v(TAG, "openPassivePort address:$connectedAddress (or ${accessPoint[0]}) port:${accessPoint[1]}")
138 val thread = Thread {
141 val tcpNoDelay = true
142 Log.v(TAG, " connect() : address:${accessPoint[0]} port:${accessPoint[1]}")
143 socketDataPort = Socket()
144 socketDataPort?.reuseAddress = true
145 socketDataPort?.keepAlive = true
146 socketDataPort?.tcpNoDelay = true
149 socketDataPort?.keepAlive = false
150 socketDataPort?.setPerformancePreferences(0, 2, 0)
151 socketDataPort?.oobInline = true
152 socketDataPort?.reuseAddress = false
153 socketDataPort?.trafficClass = 0x80
155 val dataAddress = if (connectedAddress != null) { connectedAddress } else { accessPoint[0] }
156 socketDataPort?.connect(InetSocketAddress(dataAddress, accessPoint[1].toInt()), 0)
157 dataOutputStreamDataPort = DataOutputStream(socketDataPort?.getOutputStream())
158 isConnectedDataPort = true
164 callbackReceiver.onReceivedFtpResponse("passive_data", -1, e.message?:"EXCEPTION")
177 private fun closeOutputStream()
181 dataOutputStream?.close()
182 dataOutputStreamDataPort?.close()
188 dataOutputStream = null
189 dataOutputStreamDataPort = null
192 private fun closeSocket()
197 socketDataPort?.close()
204 socketDataPort = null
207 private fun receiveDataMain()
211 // すでにコマンドのスレッド動作中なので抜ける
214 isStartDataPort = true
215 Log.v(TAG, " receiveDataMain() : START")
216 val command = FtpCommand("data", " \r\n")
217 while (isStartDataPort)
221 Log.v(TAG, " --- RECEIVE DATA STANDBY --- ")
222 sleep(DATA_POLL_QUEUE_MS)
223 receiveFromDevice(command, socketDataPort, DATA_POLL_QUEUE_MS, MAX_RETRY_WAIT_COUNT_DATA)
228 callbackReceiver.onReceivedFtpResponse("receiveDataMain", -1, e.message?:"EXCEPTION")
233 private fun sendCommandMain()
237 // すでにコマンドのスレッド動作中なので抜ける
241 Log.v(TAG, " sendCommandMain() : START")
246 val command = commandQueue.poll()
249 issueCommand(command)
250 sleep(COMMAND_POLL_QUEUE_MS)
252 Log.v(TAG, " --- RECEIVE WAIT FOR REPLY --- ")
253 receiveFromDevice(command, socket, COMMAND_POLL_QUEUE_MS, MAX_RETRY_WAIT_COUNT)
255 sleep(COMMAND_POLL_QUEUE_MS)
260 callbackReceiver.onReceivedFtpResponse("sendCommandMain", -1, e.message?:"EXCEPTION")
265 private fun receiveFromDevice(command: FtpCommand, targetSocket: Socket?, wait: Int, maxRetry : Int)
269 val byteArray = ByteArray(PACKET_BUFFER_SIZE)
270 val inputStream: InputStream? = targetSocket?.getInputStream()
271 if (inputStream == null)
273 Log.v(TAG, " InputStream is NULL... RECEIVE ABORTED.")
274 callbackReceiver.onReceivedFtpResponse("receiveFromDevice($command)", -1, "InputStream is NULL...")
278 // 初回データが受信バッファにデータが溜まるまで待つ...
279 var readBytes = waitForReceive(inputStream, wait, maxRetry)
283 Log.v(TAG, " ----- DETECT RECEIVE RETRY OVER... -----")
284 callbackReceiver.onReceivedFtpResponse("receiveFromDevice(${command.command})", -1, "Receive timeout (${COMMAND_POLL_QUEUE_MS * MAX_RETRY_WAIT_COUNT} ms)")
288 var isWriteData = false
290 val byteStream = ByteArrayOutputStream()
292 while (readBytes > 0)
294 readBytes = inputStream.read(byteArray, 0, PACKET_BUFFER_SIZE)
297 Log.v(TAG," RECEIVED MESSAGE FINISHED ($readBytes)")
300 //Log.v(TAG, " :::::::::: [${command.command}] Read Bytes: $readBytes")
301 byteStream.write(byteArray, 0, readBytes)
302 //dataValue += String(byteArray.copyOfRange(0, readBytes))
304 sleep(RECEIVE_WAIT_MS)
305 readBytes = inputStream.available()
309 //Log.v(TAG, " >>>>[${command.command}]>>>>>> $dataValue")
310 callbackReceiver.onReceivedFtpResponse(command.command, 0, String(byteStream.toByteArray()))
311 //callbackReceiver.onReceivedFtpResponse(command.command, 0, dataValue)
318 callbackReceiver.onReceivedFtpResponse("receiveFromDevice", -1, t.message?:"EXCEPTION")
322 private fun issueCommand(command: FtpCommand)
326 val byteArray = command.value.toByteArray()
327 if (byteArray.isEmpty())
330 Log.v(TAG, " SEND BODY IS NOTHING.")
331 callbackReceiver.onReceivedFtpResponse(command.command, -1, "SEND COMMAND IS NOTHING.")
334 if (dataOutputStream == null)
336 Log.v(TAG, " DataOutputStream is null.")
337 callbackReceiver.onReceivedFtpResponse(command.command, -1, "DataOutputStream is null.")
340 if (isDumpReceiveLog)
343 SimpleLogDumper.dump_bytes("SEND[" + byteArray.size + "] ", byteArray)
347 dataOutputStream?.write(byteArray)
348 dataOutputStream?.flush()
353 callbackReceiver.onReceivedFtpResponse(command.command, -1, e.message?:"EXCEPTION")
357 private fun waitForReceive(inputStream: InputStream, delayMs: Int, maxRetry: Int): Int
359 var retryCount = maxRetry
360 var isLogOutput = true
364 while (readBytes <= 0)
367 readBytes = inputStream.available()
372 // Log.v(TAG, " ----- waitForReceive:: is.available() WAIT... : " + delayMs + "ms")
390 private fun sleep(delayMs: Int)
394 Thread.sleep(delayMs.toLong())
403 private val TAG = MyFtpClient::class.java.simpleName
404 private const val COMMAND_POLL_QUEUE_MS = 15
405 private const val DATA_POLL_QUEUE_MS = 50
406 private const val MAX_RETRY_WAIT_COUNT = 20
407 private const val MAX_RETRY_WAIT_COUNT_DATA = 100
408 private const val RECEIVE_WAIT_MS = 50
409 private const val PACKET_BUFFER_SIZE = 8192
411 private const val FTP_CONTROL_PORT = 21