general_json_rpc 1.0.0 copy "general_json_rpc: ^1.0.0" to clipboard
general_json_rpc: ^1.0.0 copied to clipboard

outdated

This package will help you to invoke methods across network using any protocol This package encode and decode your requests and responses to and from bytes So you can send it any where using any proto [...]

GeneralJsonRpc #

This package will help you to invoke methods across network using any protocol

This package encode and decode your requests and responses to and from bytes so you can send it any where using any protocol

This package is implementing json-rpc v2.0

A Quick Guide #

Instead of following the guide you can see the final simple code at the Shortcuts section below.

In this guide you will create a server and client sides application that invokes methods throw tcp sockets

lets start

  • First on the server side we will define 3 methods [sum, print, quit]

    • The sum method will get numbers list and returns their sum

    • The print method will get a parameter named message and print its content on the server side

    • The quit method will end the application by executing exit(0);

Server side #

Now lets start

  • First import dart:io and package:general_json_rpc/general_json_rpc.dart

  • Now lets create the server method which will simulate the server side


void server() async {
  ...
}

and inside lets create our simple server and bind on port 8081

final server = await ServerSocket.bind(InternetAddress.anyIPv4, 8081);
print('[server] Server is running on port 8081');

cool, now let create a MethodRunner where we are going to define our rpc methods


final runner = MethodRunner();

// now lets define our 3 methods
runner.register<int>('sum', (numbers) => numbers.reduce((a, b) => a + b));
runner.register<void>('print', (p) => print(p['message']));
runner.register<void>('quit', (_) => exit(0));

cool now we defined our 3 methods

now lets listen to the incoming connections


await for(final Socket client in server) {
  client.listen(
    (bytes) async {
      // 1. lets converts bytes into [RpcObject]
      final rpcObject = RpcObject.decode(bytes);

      // 2. now lets handle the coming requests
      final result = await RpcObject.handle(
        rpcObject,
        onRequestAll: runner.executeRequest,
      );

      // 3. now lets encode the result and send it
      if (result != null) client.add(result.encode());
    },
  );
}

Now we create 3 steps to handle am incoming request

  1. The incoming request is a list of bytes Uint8List we converted it to a RpcObject using the decode method

  2. Now lets handling the incoming request by executing the methods and send back the results, and we archived this by referencing the runner.executeRequest method as a onRequestAll callback method which will be executed when ever the request is a normal method execution or notification request. The handle method returns a Future of a nullable RpcObject which the response.

    Note if no returned value then no response means no returned RpcObject by the handle method

  3. Finally we need to check if the result is not null then we can send it back to the client To send RpcObject you need to convert it to bytes, and this archived by using the RpcObject.encode() method.

Cool this was the server side, lets take a complete view

import 'dart:io';
import 'package:general_json_rpc/general_json_rpc.dart';

void main() {
  server();
}

void server() async {
  var server = await ServerSocket.bind(InternetAddress.anyIPv4, 8081);
  print('[server] Server is running on port 8081');

  // create MethodRunner to define the rpc methods
  final runner = MethodRunner();

  // now lets define the 3 methods
  runner.register<int>('sum', (numbers) => numbers.reduce((a, b) => a + b));
  runner.register<void>('print', (p) => print(p['message']));
  runner.register<void>('quit', (_) => exit(0));

  // now lets listen for new clients
  await for (final Socket client in server) {
    print('[server] new client');
    client.listen(
      (bytes) async {
        // converts bytes into [RpcObject]
        final rpcObject = RpcObject.decode(bytes);

        // now lets handle different cases
        // we will handle requests and notifications
        // requests has a return value while notifications has not
        // we will handle both using [onRequestAll] parameter
        // we will use runner.executeRequest as a callback
        // handle will return a Future of nullable [RpcObject] which is the response
        // for the current request if it has
        final response = await RpcObject.handle(
          rpcObject,
          onRequestAll: runner.executeRequest,
        );

        // now lets send the response to the client if it is not null
        // before send we must convert it to bytes using the `encode()` method
        if (response != null) client.add(response.encode());
      },
    );
  }
}

Client Side #

Cool now we can start implementing the client side. First lets create the client method which simulates the client side

void client() async {
  ...
}

now lets connect to the server


print('[client] connecting...');
final client = await Socket.connect(InternetAddress.loopbackIPv4, 8081);
print('[client] connected!');

Cool, now lets send our requests

The first method is sum

// lets find out the sum of 1, 2 and 3
final request = RpcRequest.create('sum', [1, 2, 3]);

// now lets encode it and send to the server
client.add(request.encode());

The next one is print

final request2 = RpcRequest.notify(
  'print',
  {
    'message': 'A message to be printed on server side by client',
  },
);

client.add(request2.encode());

also you can use the [] and []= operators to assign params

final request2 = RpcRequest.notify('print', {});
request2['message'] = 'A message to be printed on server side by client';
client.add(request2.encode());

Now lets see the deference between RpcRequest.create and RpcRequest.notify

both creates new instance of RpcRequest but create creates instance with a generated id while notify creates instance with null as a id value

If we expect a returned value from the requested method then we need to specify an id for our request so later we will listen and filter the responses to find one with the same id to get our returned value

Cool, now lets see the last one which is quit

// We do not expect a returned value so we will use `notify`
// no need to pass params
final request3 = RpcRequest.notify('quit');

// encode and send
client.add(request3.encode());

Do not forget to call server and client methods in the main method

void main() {
  server();
  client();
}

Now lets get a complete view for what we did so far

import 'dart:io';
import 'package:general_json_rpc/general_json_rpc.dart';

void main() async {
  server();
  client();
}

void server() async {
  var server = await ServerSocket.bind(InternetAddress.anyIPv4, 8081);
  print('[server] Server is running on port 8081');

  // create MethodRunner to define the rpc methods
  final runner = MethodRunner();

  // now lets define the 3 methods
  runner.register<int>('sum', (numbers) => numbers.reduce((a, b) => a + b));
  runner.register<void>('print', (p) => print(p['message']));
  runner.register<void>('quit', (_) => exit(0));

  // now lets listen for new clients
  await for (final Socket client in server) {
    print('[server] new client');
    client.listen(
      (bytes) async {
        // converts bytes into [RpcObject]
        final rpcObject = RpcObject.decode(bytes);

        // now lets handle different cases
        // we will handle requests and notifications
        // requests has a return value while notifications has not
        // we will handle both using [onRequestAll] parameter
        // we will use runner.executeRequest as a callback
        // handle will return a Future of nullable [RpcObject] which is the response
        // for the current request if it has
        final response = await RpcObject.handle(
          rpcObject,
          onRequestAll: runner.executeRequest,
        );

        // now lets send the response to the client if it is not null
        // before send we must convert it to bytes using the `encode()` method
        if (response != null) client.add(response.encode());
      },
    );
  }
}

void client() async {
  print('[client] connecting...');
  final client = await Socket.connect(InternetAddress.loopbackIPv4, 8081);
  print('[client], connected!');

  // now lets first request the `sum` method
  // we expect a returned value
  // so we will send a rpc request
  // we will pass 3 numbers 1, 2, 3 in a list
  final request = RpcRequest.create('sum', [1, 2, 3]);

  // now lets send the request to the server
  // first encode it using the `encode()` method then send it
  client.add(request.encode());

  // ============================= //
  // now lets call the `print` method
  // we expect no returned value
  // so we will send a rpc notification
  final request2 = RpcRequest.notify('print', {});

  // I created the param as an empty Map
  // to show you how to add extra parameters to the request
  // I am going to define key `message` and add my message to it
  request2['message'] =
      'A message to be printed on the server side by the client';

  // now lets encode and send the notification request
  client.add(request2.encode());

  // ============================= //
  // now lets shutdown the server by calling the `quit` method
  // we expect no returned value
  // so we will send a rpc notification
  client.add(RpcRequest.notify('quit').encode());
}

Cool we are done, but if we run our code the sum method example will not work as expected because we need to listen for the server returned values on our client first.

Listen for responses #

First we need to handle responses at client side

client.listen(
  (bytes) {
    final rpcObject = RpcObject.decode(bytes);

    RpcObject.handle(
      rpcObject,
      onResponse: RpcResponseManager.global.handleResponse,
    );
  },
)

Cool now we are listening to any response and handling it on the global RpcResponseManager object.

Now lets handle the sum method results

final request = RpcRequest.create('sum', [1, 2, 3]);

client.add(request.encode());

request.waitForResponse().then((result) {
    print('[client] sum result: $result');
  }).onError<RpcError>((error, _) {
    print('[client] error: ${error.message}');
  });

also you can handle it like this

final sum = await request.waitForResponse();

Do not forget we are sending a notification request with method quit to exit the app at the end. But now our app needs to wait until we get the response back so we can comment it or send it after we receive the result

request.waitForResponse().then((result) {
    print('[client] sum result: $result');
    client.add(RpcRequest.notify('quit').encode());
  }).onError<RpcError>((error, _) {
    print('[client] error: ${error.message}');
  });

Shortcuts #

This is a lot of code now lets clean and make it simple

import 'dart:io';
import 'package:general_json_rpc/general_json_rpc.dart';

void main() {
  server();
  client();
}
void server() async {
  var server = await ServerSocket.bind(InternetAddress.anyIPv4, 8081);
  print('[server] Server is running on port 8081');

  // create MethodRunner to define the rpc methods
  final runner = MethodRunner();

  // now lets define the 3 methods
  runner.register<int>('sum', (numbers) => numbers.reduce((a, b) => a + b));
  runner.register<void>('print', (p) => print(p['message']));
  runner.register<void>('quit', (_) => exit(0));

  // now lets listen for new clients
  await for (final Socket client in server) {
    print('[server] new client');
    client.listen(
      (bytes) async {
        // converts bytes into [RpcObject]
        final rpcObject = RpcObject.decode(bytes);

        // The `auto` method handles every thing for you
        // Will handle request using [runner] and responses using [RpcResponseManager.global]
        final response = await RpcObject.auto(rpcObject, methodRunner: runner);

        // now lets send the response to the client if it is not null
        // before send we must convert it to bytes using the `encode()` method
        if (response != null) client.add(response.encode());
      },
    );
  }
}
void client() async {
  print('[client] connecting...');
  final client = await Socket.connect(InternetAddress.loopbackIPv4, 8081);
  print('[client] connected!');

  // lets listen for data from the server
  client.listen(
    (bytes) async {
      // converts bytes into [RpcObject]
      final rpcObject = RpcObject.decode(bytes);

      // The `auto` method handles every thing for you
      // ! Only will handle responses using [RpcResponseManager.global]
      RpcObject.auto(rpcObject);

      // we are not going to send any thing back
      // ! so we do not need to add the next line
      // if (response != null) client.add(response.encode());
    },
  );

  // ============================= //
  final request2 = RpcRequest.notify('print', {});

  request2['message'] =
      'A message to be printed on the server side by the client';

  client.add(request2.encode());

  // ============================= //
  final request = RpcRequest.create('sum', [1, 2, 3]);
  client.add(request.encode());

  // now lets register the request to be notified on its result
  final sum = await request.waitForResponse();
  print('[client] sum result: $sum');

  // ============================= //
  client.add(RpcRequest.notify('quit').encode());
}
0
likes
0
points
47
downloads

Publisher

unverified uploader

Weekly Downloads

This package will help you to invoke methods across network using any protocol This package encode and decode your requests and responses to and from bytes So you can send it any where using any protocol This package is implementing json-rpc v2.0

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

rxdart

More

Packages that depend on general_json_rpc