capnproto / pycapnp

Cap'n Proto serialization/RPC system - Python bindings
BSD 2-Clause "Simplified" License
458 stars 125 forks source link

Serialize and deserialize data over RPC #353

Closed Sabbirshubho closed 5 months ago

Sabbirshubho commented 5 months ago

I want to make a client-server application. where upon a request server will build the message then serialize the message and send it to client. Client will deserialize the received message. This is my capnp file.

   struct Info {
       width @0 : Int32;
       height @1 : Int32;
    }

    struct Image {
      info @0 : Info;
      buffer @1 : Data;
    }

    interface MyService {
      sendMessage @0 (request :Image) -> (response :Data);
    }

My server side implementation looks like this:

        import capnp
        import asyncio
        from time import time
        capnp.remove_import_hook()
        myservice_capnp = capnp.load('myservice.capnp')

        def getServerDataResponse():
            image = myservice_capnp.Image.new_message()
            Info = image.init("Info")
            Info.width = 20
            Info.height = 20
            image.buffer = b'BinaryImageDataHere'
            msg_bytes = image.to_bytes() # serializing data
            return msg_bytes

        class MyServiceImpl(myservice_capnp.MyService.Server):
            async def sendMessage(self, request, **kwargs):
                server_time = time()
                img = getServerDataResponse()
                return img

        async def new_connection(stream):
            await capnp.TwoPartyServer(stream, bootstrap=MyServiceImpl()).on_disconnect()

        async def main():
            server = await capnp.AsyncIoStream.create_server(new_connection, 'localhost', '60000')
            async with server:
                await server.serve_forever()

        if __name__ == "__main__":
            asyncio.run(capnp.run(main()))

In my client side implementation look like this:

import capnp
import asyncio
from time import time
capnp.remove_import_hook()
myservice_capnp = capnp.load('myservice.capnp')

def getClientDataRequest():
    return myservice_capnp.Image()

async def main(connection):
    client = capnp.TwoPartyClient(connection)
    my_service = client.bootstrap().cast_as(myservice_capnp.MyService)

    for i in range(3):
        data_request = getClientDataRequest()
        eval_promise = my_service.sendMessage(data_request)
        response = await eval_promise
        print(f" response type{type(response)}")
        deserialize = myservice_capnp.Image.from_bytes(response)

async def cmd_main():
    await main(await capnp.AsyncIoStream.create_connection(host='localhost', port='60000'))

if __name__ == "__main__":
    asyncio.run(capnp.run(cmd_main()))

As I am serializing the data before sending thats why I kept the response from sendMessage() as Data in .capnp file. The problem is I receive the response in client as capnp.lib.capnp._Response object which doesn't contain the data. how to extract the data in that case? My goal is send serialize data over rpc and deserialize it afterwards. I am using v2.0 beta version. Thanks in advance.

darren-harton commented 5 months ago

Try this:

eval_promise = my_service.sendMessage(data_request)
response = (await eval_promise).response

If I understand correctly, _Response inherits from _DynamicStructReader which can access your data.

Sabbirshubho commented 5 months ago

Yes you are right. I missed that part.