壁打ちAtCoder

AtCoderの問題をひたすら解いてくブログです。思考やコードの書き方の私的備忘録として

Python(TCP server) -> Android/Kotlin(TCP client)へ画像送信

ひとまず送れたということで殴り書き。
やりたいことを成し遂げるまでは課題は山のようにある。

・分割しなくても1度に送受信できる大きさの画像(1773Byte)
・画像のパスはpythonのコードと同じ階層

Python

from PIL import Image
import socketserver
import io

#ひとまず同じ階層に置いた
img = Image.open('palm32.png')


img_bytes = io.BytesIO()
img.save(img_bytes,format="PNG")
img_bytes = img_bytes.getvalue()

#print(len(img_bytes))  #len(img_bytes)でバイトサイズを取得する


class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(img_bytes) #<-画像を送信
        #y = bytearray([0x01,0x02,0x03,0x04])
        #print(y)
        #self.request.sendall(y)

if __name__ == "__main__":
    HOST, PORT = '127.0.0.1', 2001

    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        server.serve_forever()
        

Kotlin

package xxx

import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import java.nio.ByteBuffer

const val MSG_CONNECTION_SUCCESS = 111 // 接続成功
const val MSG_CONNECTION_FAILED = 222  // 接続失敗
const val MSG_IOEXCEPTION = 333        // 例外発生

class MainActivity : AppCompatActivity() {
    private var tcpcom: ComTcpClient? = null
    val ip = "10.0.2.2"
    val port = "2001"  
    private val TAG = MainActivity::class.java.simpleName

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        connect()

        val imgView = findViewById<ImageView>(R.id.imageView)
        val button = findViewById<Button>(R.id.sendButton)
        button.setOnClickListener{
            //todo なんかメッセージ送る
            val sendArray = ByteBuffer.allocate(30)
            sendArray.putInt(0x01020304)
            sendArray.putInt(0x05060708)
            val byteArray = ByteBuffer.allocate(640000)
            try {
                tcpcom?.sendOrReceive { outputStream, inputStream ->
                    outputStream.write(sendArray.array())
                    inputStream.read(byteArray.array())

                    //todo whileで受信し続ける必要がある
                    /* ByteArrayOutputStream
                    BitmapFactory.decodeByteArray()*/

                    //println("受信メッセージ:"+byteArray.getInt())
                    val img = BitmapFactory.decodeByteArray(byteArray.array(),0,byteArray.array().size)
                    print(img)
                }
                //imgView.setImageBitmap(img)
            }
            catch(e: Exception){
                Log.d(TAG,"Failed...")
            }
        }

        val channel = Channel<Int>()
        GlobalScope.launch(Dispatchers.Main) {
            when (channel.receive()) {
                MSG_CONNECTION_SUCCESS -> {
                    Log.d(TAG,"Success!")
                }
                MSG_CONNECTION_FAILED -> {
                    Log.d(TAG,"Failed...")
                    // エラー処理
                }
                MSG_IOEXCEPTION -> {
                    Log.d(TAG,"Exception!")
                    //エラー処理
                }
            }
        }
    }

    fun connect(){
        val channel = Channel<Int>()
        if (!ip.isEmpty() && !port.isEmpty()) {
            tcpcom = ComTcpClient(ip, port.toInt(), channel)
            tcpcom?.connect()
        }
    }
}

Client側でinputStreamをループさせ続けることで大きなサイズの画像も受信することができた。1MB程度まで確認。
なおServer側は画像のbytearrayを一度で投げている。
受信もといループの画像サイズは手動設定。

            try {
                tcpcom?.sendOrReceive { outputStream, inputStream ->
                    outputStream.write(sendArray.array())
                    while(byteSize<1400000) {
                        inputStream.read(byteArray.array())

                        allByteArray.write(byteArray.array())
                        byteSize += 1024


                    }
                    //println("受信メッセージ:"+byteArray.getInt())
                    val img = BitmapFactory.decodeByteArray(allByteArray.toByteArray(),0,allByteArray.toByteArray().size)
                    handler.post{
                        imgView.setImageBitmap(img)
                    }
                    print(img)
                }
                //imgView.setImageBitmap(img)
            }

f:id:Rgrayjourney:20210908201954p:plain
handlerを使ってviewの更新もできた。一歩前進。
実際はサイズ調整も必要になりそう。


参考
Python:
qiita.com


Kotlin:
qiita.com