Search This Blog

Sunday, March 23, 2014

Remote Procedure Calls between Android and .NET

Simple example showing Remote Procedure Calls between Android and .NET based on TCP.
And few words about RPC vs. Messaging.

The source code for this example can be downloaded from:
http://www.eneter.net/Downloads/Examples/ClientServerRpc.zip

Introduction

The example bellow demonstrates a simple client-server communication between Android and .NET using RPC (Remote Procedure Calls). The .NET application is a service exposing the interface and the Android application is a client calling remote methods from the interface.

The example is based on Eneter Messaging Framework. The framework is free and can be downloaded from http://www.eneter.net/ProductDownload.htm.
More technical info about the framework can be found at http://www.eneter.net/ProductInfo.htm.

Remote Procedure Calls vs Messaging

RPC is a request-response communication scenario where one application (typically client) requests to invoke a method in another application (typically service) and waits until the invoked method returns.

The difference between RPC and messaging is that RPC is functionality oriented and the messaging is message oriented. It means RPC models the interprocess communication from the perspective of required functionality and the messaging models it from the perspective of messages flowing between applications.

Therefore the RPC communication model specifies functions/methods which are exposed by a service and can be executed from clients. It is typically synchronous request-response interaction with one response per request.

Modeling the communication via messaging is different. Instead of functionality it focuses on messages and also how these messages are routed between applications. It does not couple the interprocess interaction with execution of certain methods. This allows to model various communication scenarios. E.g. request-response with zero, one or multiple responses per request or e.g. publish-subscribe communication. The communication is typically asynchronous.

It is obvious the messaging provides more possibilities in designing interprocess communication than RPC. The price for possibilities is the complexity. In some cases using the messaging can be an overkill. E.g. asynchronous programming is more complicated and error prone than synchronous. On the other hand (ab)using RPC for advanced communication scenarios can result into workarounds and poor quality.

Eneter Messaging Framework provides functionality for both approaches. Messaging as well as RPC. In fact RPC is implemented using the messaging.
From the framework concept perspective RPC is part of end-points. It provides RpcClient and RpcService components which can attach and detach communication channels as any other messaging component.



The goal of RPC in Eneter Messaging Framework is not to pretend the interprocess communication is like a local call.
The goal is to provide a convenience for simple request-response scenarios. RPC allows to specify the service API by declaring a method interface (instead of declaring multiple message types and describing the communication protocol) and allows to perform request-response communication synchronously (instead of receiving response messages asynchronously).
In addition RPC in Eneter Messaging Framework works across multiple platforms. It means you can use this technique e.g. between Java and .NET or between Android and .NET as demonstrated in the example bellow.

To Run Example Code

1. Download Eneter

The example source code from this article does not contain Eneter libraries. Therefore you need to download Eneter Messaging Framework for .NET and also for Android from http://www.eneter.net/ProductDownload.htm.

2. Add Eneter Libraries to the project

When you have downloaded Eneter libraries you need to add them to projects. Add Eneter.Messaging.Framework.dll to the RpcService solution and eneter-messaging-android.jar into the AndroidRpcClient project.

Please follow this procedure (for Eclipse) to add libraries into the Android project: (To add a library into the project you need to import it instead of adding it via project properties. Also ensure Java compliance level is set to 6.0. Properties -> Java Compiler -> JDK Compliance -> 1.6.) Then you need to add Eneter libraries to projects and compile it.
To add eneter to the Android project please follow this procedure for Eclipse:

  1. Create a new folder 'libs' in your project. (use exactly name libs)
  2. Right click on 'libs' and choose 'Import...' -> 'General/File System' -> 'Next'.
  3. Then click 'Browser' button for 'From directory' and navigate to directory with libraries you want to add.
  4. Select check boxes for libraries you want to add.
  5. Press 'Finish'

3. Compile and Run

When you have added Eneter libraries into the project you can compile it and run it.
First execute RpcService and then AndroidRpcClient.

TCP on Android

When you implement the communication via TCP on Android, you must count with two specifics:

1. You must set INTERNET permission for your Android application!

If the permission is not set, the application is not allowed to communicate across the network. To set the INTERNET permission you must add the following line to AndroidManifest.xml.

<uses-permission android:name="android.permission.INTERNET"/>

An example of AndroidManifest.xml allowing communication across the network:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="eneter.androidrpcclient"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="eneter.androidrpcclient.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

2. The IP address 127.0.0.1 (loopback) cannot be set on the Android emulator to communicate with the .NET application!

The emulator acts as a separate device. Therefore, the IP address 127.0.0.1 is the loopback of that device and cannot be used for the communication with other applications running on the same computer as the emulator.

Instead of that you must use a real IP address of the computer or the emulator can use the special address 10.0.2.2 that is routed to 127.0.0.1 (loopback) on the computer. In my example, the Android emulator uses 10.0.2.2 and the .NET service is listening to 127.0.0.1.

.NET Service application

The service is a simple console application exposing a simple IMyService interface. The service is exposed using the RpcService component. RpcService then attaches the input channel and starts to listen to requests via TCP. When a request is received it is routed to the service method and the return value is then send back to the client.

The whole implementation is very simple:

using System;
using Eneter.Messaging.EndPoints.Rpc;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;

namespace Service
{
    // Service interface.
    public interface IMyService
    {
        int Calculate(int a, int b);
        string GetEcho(string text);
    }

    // Service implementation.
    public class MyService : IMyService
    {
        public int Calculate(int a, int b)
        {
            int aResult = a + b;
            Console.WriteLine("{0} + {1} = {2}", a, b, aResult);

            return aResult;
        }

        public string GetEcho(string text)
        {
            Console.WriteLine("Received text = {0}", text);

            return text;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Use single-instance service.
            // It means all connected clients share the same instance of the service.
            // Note: if needed you can choose per-client-instance mode too.
            IRpcFactory aFactory = new RpcFactory();
            IRpcService<IMyService> aService =
                aFactory.CreateSingleInstanceService<IMyService>(new MyService());

            // Use TCP for the communication.
            // You also can use other protocols e.g. WebSockets.
            IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
            IDuplexInputChannel anInputChannel =
                aMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:8032/");

            // Attach the input channel to the RpcService and start listening.
            aService.AttachDuplexInputChannel(anInputChannel);

            Console.WriteLine("RPC service is running. Press ENTER to stop.");
            Console.ReadLine();

            // Detach the input channel and release the listening thread.
            aService.DetachDuplexInputChannel();
        }
    }
}

Android Client Application

The client is a simple Android application which declares the same IMyService interface as the .NET service application. The IMyservice interface type is then provided to RpcClient which generates the proxy. The proxy is retrieved by myRpcClient.getProxy(). The retrieved proxy allows to call interface methods. The calls are then routed to the service where they are processed.

Here is the whole client implementation:

package eneter.androidrpcclient;

import eneter.messaging.diagnostic.EneterTrace;
import eneter.messaging.endpoints.rpc.*;
import eneter.messaging.messagingsystems.messagingsystembase.*;
import eneter.messaging.messagingsystems.tcpmessagingsystem.TcpMessagingSystemFactory;
import android.os.Bundle;
import android.app.Activity;
import android.view.*;
import android.view.View.OnClickListener;
import android.widget.*;

public class MainActivity extends Activity
{
    // Interface exposed by the service.
    // Note: names of methods are case sensitive.
    // So keep capitals as declared in C#.
    public static interface IMyService
    {
        int Calculate(int a, int b);

        String GetEcho(String text);
    }

    // UI controls
    private EditText myNumber1EditText;
    private EditText myNumber2EditText;
    private EditText myResultEditText;
    private Button myCalculateButton;
    private EditText myEchoEditText;
    private Button myGetEchoButton;
    private EditText myEchoedEditText;

    // Eneter communication.
    IRpcClient<IMyService> myRpcClient;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get UI widgets.
        myNumber1EditText = (EditText) findViewById(R.id.number1editText);
        myNumber2EditText = (EditText) findViewById(R.id.number2EditText);
        myResultEditText = (EditText) findViewById(R.id.resultEditText);
        myEchoEditText = (EditText) findViewById(R.id.echoTexteditText);
        myEchoedEditText = (EditText) findViewById(R.id.echoedEditText);

        myCalculateButton = (Button) findViewById(R.id.caculateBtn);
        myCalculateButton.setOnClickListener(myOnCalculateButtonClick);

        myGetEchoButton = (Button) findViewById(R.id.getEchoBtn);
        myGetEchoButton.setOnClickListener(myOnGetEchoButtonClick);

        // Open the connection in another thread.
        // Note: From Android 3.1 (Honeycomb) or higher
        // it is not possible to open TCP connection
        // from the main thread.
        Thread anOpenConnectionThread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                openConnection();
            }
        });
        anOpenConnectionThread.start();
    }

    @Override
    public void onDestroy()
    {
        closeConnection();

        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    private void openConnection()
    {
        try
        {
            // Instantiate RPC client.
            RpcFactory aFactory = new RpcFactory();
            myRpcClient = aFactory.createClient(IMyService.class);

            // Use TCP for the communication.
            IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();

            // Note: special IP address used by the Android simulator.
            // 10.0.2.2 is routed by the simulator to 127.0.0.1 of machine where
            // the simulator is running.
            // Use real IP address if you run it on a real Android device!
            IDuplexOutputChannel anOutputChannel = aMessaging
                    .createDuplexOutputChannel("tcp://10.0.2.2:8032/");

            // Attach the output channel to the RPC client and be able to
            // communicate.
            myRpcClient.attachDuplexOutputChannel(anOutputChannel);
        }
        catch (Exception err)
        {
            EneterTrace.error("Failed to open connection with the service.",
                    err);
        }
    }

    private void closeConnection()
    {
        // Stop listening to response messages.
        if (myRpcClient != null)
        {
            myRpcClient.detachDuplexOutputChannel();
        }
    }

    private void onCalculateButtonClick(View v)
    {
        int aNumber1 = Integer.parseInt(myNumber1EditText.getText().toString());
        int aNumber2 = Integer.parseInt(myNumber2EditText.getText().toString());

        try
        {
            // Invoke the remote method.
            int aResult = myRpcClient.getProxy().Calculate(aNumber1, aNumber2);

            myResultEditText.setText(Integer.toString(aResult));
        }
        catch (Exception err)
        {
            EneterTrace.error("Failed to invoke the remote method.", err);
        }
    }

    private void onGetEchoButtonClick(View v)
    {
        try
        {
            String aText = myEchoEditText.getText().toString();
            String anEchoedText = myRpcClient.getProxy().GetEcho(aText);

            myEchoedEditText.setText(anEchoedText);
        }
        catch (Exception err)
        {
            EneterTrace.error("Failed to invoke the remote method.", err);
        }
    }

    private OnClickListener myOnCalculateButtonClick = new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            onCalculateButtonClick(v);
        }
    };

    private OnClickListener myOnGetEchoButtonClick = new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            onGetEchoButtonClick(v);
        }
    };
}


13 comments:

  1. RPC is also a interesting way for communication. Is it possible to use RPC with .net in
    async request/response mode? Additional is it possible to use messaging framework and rpc with a channelwrapper together?

    ReplyDelete
    Replies
    1. Async Request/Response
      If I understand correctly you mean if it is possible that the implementation of the proxy interface will offer also possibility to execute methods asynchronously.
      This is currently not possible. However you can achieve it very easily. E.g. in case of Java you can use Futures. The code would look like:


      // Initializing the thread pool.
      ExecutorService aThreadPool = Executors.newFixedThreadPool(400);

      ...

      // Executing the service method asynchronously.
      Future aFuture = aThreadPool.submit(new Callable()
      {
      @Override
      public Integer call() throws Exception
      {
      // Invoke the remote method.
      return myRpcClient.getProxy().Calculate(aNumber1, aNumber2);
      }
      });

      ...

      // Get the result.
      int aResult = aFuture.get();

      Channel Wrappers
      Yes, you can use RPC and ChannelWrapper/Unwrapper together.

      Delete
  2. Thank you for your fast answer. I try to put the messaging and rpc functionaltity together. Service should listening only at one single tcp port but i can't figure out how to do this. There is also a .net calculatorservice sample with wrapper functionality.
    Can you please show me a example how to implement this as a .net service?

    ReplyDelete
    Replies
    1. So if I understand correctly you would like to have a service that will be accessible via one port and by both approaches: messaging and RPC.
      Is that correct?

      Delete
  3. Yes, this is correct ...

    ReplyDelete
    Replies
    1. Here you can download an example project that should implement such scenario:
      Example

      Delete
  4. using this exempel, message result in .net is:

    StringToItemList<string xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">{&apos;name&apos;:&apos;Babs list&apos;,&apos;Id&apos;:&apos;0&apos;,&apos;CreatedDate&apos;:&apos;2014-11-19 05:57:57.316&apos;,&apos;State&apos;:&apos;true&apos;}</string>101

    I expect somting like this:
    {"Id":0, "name":"Babs list", "CreatedDate":2014-11-19}

    why?

    Within android using com.google.gson.Gson

    ReplyDelete
  5. hi Sir, I am trying to develop an application in android which can communicate with .net app through usb data cable. Like, If i click a button in android app, .net app should receive some command and should take some action based on that.Main thing is that communication should be through usb cable. Please give your suggestions. Waiting for your valuable response.

    ReplyDelete
    Replies
    1. Hi Kannadiga,
      If I understand correctly you need the client-service communication between Android (client) and a .NET (service).

      Eneter supports the communication via the USB cable however there is a restriction on the Android side - when it is connected via the USB cable then from the communication perspective Android expects to be a service (it does not open the connection but it listens to requests).
      Therefore you need to implement so called reversed communication for your scenario.
      Please download the example code:
      www.eneter.net/Downloads/Examples/AndroidUsbReverse.zip

      Delete
  6. Can we do it vice versa i want to send a notification from C# standalone application to android app which will be connected in same network over wifi.

    ReplyDelete
    Replies
    1. Yes, you can do it. In general there are three possibilities:

      1. Eneter RPC works also with events. It means the RPC interface exposed by the service can declare events too. E.g.:

      // Service interface.
      public interface IMyService
      {
      // Some notification event.
      event EventHandler<YourEventArgs> YourEventOccured;

      int Calculate(int a, int b);
      string GetEcho(string text);
      }

      then in Java client must be the matching declaration:

      public static interface IMyService
      {
      Event<YourEventArgs> YourEventOccured();

      int Calculate(int a, int b);

      String GetEcho(String text);
      }


      2. You can implement it other way around. It means the Android application will be the service and the .NET application will be the client.

      3. If using the RPC is not a must for you then you can consider to communicate via messages. In such case you can send messages from the service to the client. Or you also can consider to use the Broker component from Eneter which provides the publish-subscribe scenarios.

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. how to make an android application input channel. can you help me with some code

    ReplyDelete