精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
本文不适合初学者,适合有半年一年工作经验的朋友,对复杂环境能理解的朋友来学习跨平台序列化。熟悉MFC的CArchive类的朋友相对好理解些。
在跨平台(例如Android和.NET之间)进行通信时,进程间通信所面临的挑战之一是如何对消息进行编码(序列化),以便两种平台都能够理解它们。由平台提供的默认二进制序列化程序不兼容,因此常见的解决方案是将消息编码为某种文本格式,例如XML或JSON。在许多情况下,这可能完全可以,但对于使用文本格式的期望高性能的应用程序来说,可能不是最佳解决方案。
下面的示例演示了如何在Android和.NET应用程序的进程间通信中使用Protocol Buffers二进制序列化。
为了构建示例源代码,您需要将对相关库的引用添加到项目中。要获得这些库,你可以下载:
Eneter.ProtoBuf.Serializer – 适用于Eneter的协议缓冲器序列化程序,它还包含已编译的协议缓冲区库和'proto'文件的实用程序应用程序。
Eneter.Messaging.Framework - 通信框架。
Protocol Buffers库是开源项目,可以在以下网址找到:
protobuf - Google针对Java,C ++和Python的Protocol Buffers实现。
protobuf-net - 用于.NET平台的Marc Gravell的协议缓冲区实现。
Eneter.ProtoBuf.Serializer - 集成Protocol Buffers和Eneter Messaging Framework的开源项目。
进入.NET项目:
protobuf-net.dll - 由Marc Gravell开发的协议缓冲,适用于.NET, Windows Phone,Silverlight和Compact Framework。
Eneter.ProtoBuf.Serializer.dll - 使用protobuf-net.dll为Eneter Messaging Framework实现序列化程序。
Eneter.Messaging.Framework.dll - 用于进程间通信的轻量级跨平台框架。
进入Android项目:
protobuf.jar - 由Google开发的用于Java和Android的协议缓冲区序列化程序。
eneter-protobuf-serializer.jar - 使用Google的protobuf.jar实现Eneter Messaging Framework的序列化程序。
eneter-messaging.jar - 用于进程间通信的轻量级跨平台框架。
重要提示:请按照此过程(为Eclipse)中添加库到Android项目:
(添加库到项目,您需要导入它,而不是通过项目属性添加。
此外,还要确保Java兼容6.0。属性- > Java编译器 - > JDK合规性 - > 1.6。)
在您的项目中创建一个新的文件夹'libs'。(使用完全名称库)
右键点击'libs'并选择'Import ...' - >'General / File System' - >'Next'。
然后点击“从目录”的“浏览器”按钮并导航到包含要添加的库的目录。
选择您想要添加的库的复选框。
按'完成'
协议缓冲区是Google最初开发的一种二进制序列化,用于在使用Java,C ++和Python等不同语言开发的应用程序之间共享数据。它成为开源软件,并被移植到其他语言和平台上。
协议缓冲区的最大优点是性能和多平台可用性,这得它在设计应用程序之间的通信时成为可选目标。
如果您有兴趣,可以在https://code.google.com/p/eneter-protobuf-serializer/wiki/PerformanceMeasurements上找到简单的性能测量。
以下过程针对跨平台通信定义消息进行了优化:(
如果您只想在.NET中使用协议缓冲区,则不必通过'proto'文件声明消息,但可以直接在源代码中声明它们归类 - 与使用DataContractSerializer相同。)
在'proto'文件中声明消息。
将'proto'文件编译为源代码(C#和Java)。它将声明的消息转换为包含指定字段和序列化功能的类。
将生成的源文件包含到C#和Java项目中。
初始化Eneter通信组件以供使用EneterProtoBufSerializer。
下面的示例与我之前的文章Android完全相同:如何通过TCP与.NET应用程序进行通信。唯一的区别是本文中的代码使用EneterProtoBufSerializer代替XmlStringSerializer。
请参考Android:如果您需要有关如何在Android上使用TCP以及如何在模拟器中设置IP地址的详细信息,请通过TCP与.NET应用程序进行通信。
'proto'文件表示描述应该用于交互的消息的协议。消息在平台中声明为中立protocol buffer language- 有关语法的详细信息,请参阅https://developers.google.com/protocol-buffers/docs/proto。
我们示例中的消息在MessageDeclarations.proto文件中声明:
// Response Message
message MyResponse
{
required int32 Length = 1;
}
然后将'proto'文件编译为C#和Java源代码。声明的消息被转换为包含声明字段和序列化功能的类。
我们的例子中使用了以下命令来编译'proto'文件:
Android客户端是一个非常简单的应用程序,允许用户放置一些文本消息并将请求发送到服务以获取文本的长度。
收到响应消息时,必须将其封送到UI线程以显示结果。
客户使用EneterProtoBufSerializer。它在openConnection()方法中实例化序列化程序,并将其引用提供给DuplexTypedMessagesFactory以保证消息发送方将使用协议缓冲区。
整个实施非常简单:
import message.declarations.MessageDeclarations.*;
import eneter.messaging.dataprocessing.serializing.ISerializer;
import eneter.messaging.diagnostic.EneterTrace;
import eneter.messaging.endpoints.typedmessages.*;
import eneter.messaging.messagingsystems.messagingsystembase.*;
import eneter.messaging.messagingsystems.tcpmessagingsystem.TcpMessagingSystemFactory;
import eneter.net.system.EventHandler;
import eneter.protobuf.ProtoBufSerializer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.*;
public class AndroidNetCommunicationClientActivity extends Activity
{
// UI controls
private Handler myRefresh = new Handler();
private EditText myMessageTextEditText;
private EditText myResponseEditText;
private Button mySendRequestBtn;
// Sender sending MyRequest and as a response receiving MyResponse.
private IDuplexTypedMessageSender<MyResponse, MyRequest> mySender;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get UI widgets.
myMessageTextEditText = (EditText) findViewById(R.id.messageTextEditText);
myResponseEditText = (EditText) findViewById(R.id.messageLengthEditText);
mySendRequestBtn = (Button) findViewById(R.id.sendRequestBtn);
// Subscribe to handle the button click.
mySendRequestBtn.setOnClickListener(myOnSendRequestClickHandler);
// 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()
{
try
{
openConnection();
}
catch (Exception err)
{
EneterTrace.error("Open connection failed.", err);
}
}
});
anOpenConnectionThread.start();
}
@Override
public void onDestroy()
{
// Stop listening to response messages.
mySender.detachDuplexOutputChannel();
super.onDestroy();
}
private void openConnection() throws Exception
{
// Instantiate Protocol Buffer based serializer.
ISerializer aSerializer = new ProtoBufSerializer();
// Create sender sending MyRequest and as a response receiving MyResponse
// The sender will use Protocol Buffers to serialize/deserialize messages.
IDuplexTypedMessagesFactory aSenderFactory = new DuplexTypedMessagesFactory(aSerializer);
mySender = aSenderFactory.createDuplexTypedMessageSender(MyResponse.class, MyRequest.class);
// Subscribe to receive response messages.
mySender.responseReceived().subscribe(myOnResponseHandler);
// Create TCP messaging for the communication.
// Note: 10.0.2.2 is a special alias to the loopback (127.0.0.1)
// on the development machine.
IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
IDuplexOutputChannel anOutputChannel
= aMessaging.createDuplexOutputChannel("tcp://10.0.2.2:8060/");
//= aMessaging.createDuplexOutputChannel("tcp://192.168.1.102:8060/");
// Attach the output channel to the sender and be able to send
// messages and receive responses.
mySender.attachDuplexOutputChannel(anOutputChannel);
}
private void onSendRequest(View v)
{
// Create the request message using ProtoBuf builder pattern.
final MyRequest aRequestMsg = MyRequest.newBuilder()
.setText(myMessageTextEditText.getText().toString())
.build();
// Send the request message.
try
{
mySender.sendRequestMessage(aRequestMsg);
}
catch (Exception err)
{
EneterTrace.error("Sending the message failed.", err);
}
}
private void onResponseReceived(Object sender,
final TypedResponseReceivedEventArgs<MyResponse> e)
{
// Display the result - returned number of characters.
// Note: Marshal displaying to the correct UI thread.
myRefresh.post(new Runnable()
{
@Override
public void run()
{
myResponseEditText.setText(Integer.toString(e.getResponseMessage().getLength()));
}
});
}
private EventHandler<TypedResponseReceivedEventArgs<MyResponse>> myOnResponseHandler
= new EventHandler<TypedResponseReceivedEventArgs<MyResponse>>()
{
@Override
public void onEvent(Object sender,
TypedResponseReceivedEventArgs<MyResponse> e)
{
onResponseReceived(sender, e);
}
};
private OnClickListener myOnSendRequestClickHandler = new OnClickListener()
{
@Override
public void onClick(View v)
{
onSendRequest(v);
}
};
}
.NET服务是一个简单的控制台应用程序,用于侦听TCP并接收请求来计算给定文本的长度。
该服务使用EneterProtoBufSerializer。它实例化序列化器并将其引用提供给DuplexTypedMessagesFactory以保证消息接收器将使用协议缓冲器来反序列化传入消息并序列化响应消息。
整个实施非常简单:
namespace ServiceExample
{
class Program
{
private static IDuplexTypedMessageReceiver<MyResponse, MyRequest> myReceiver;
static void Main(string[] args)
{
// Instantiate Protocol Buffer based serializer.
ISerializer aSerializer = new ProtoBufSerializer();
// Create message receiver receiving 'MyRequest' and receiving 'MyResponse'.
// The receiver will use Protocol Buffers to serialize/deserialize messages.
IDuplexTypedMessagesFactory aReceiverFactory =
new DuplexTypedMessagesFactory(aSerializer);
myReceiver = aReceiverFactory.CreateDuplexTypedMessageReceiver<MyResponse, MyRequest>();
// Subscribe to handle messages.
myReceiver.MessageReceived += OnMessageReceived;
// Create TCP messaging.
IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
IDuplexInputChannel anInputChannel
= aMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:8060/");
// Attach the input channel and start to listen to messages.
myReceiver.AttachDuplexInputChannel(anInputChannel);
Console.WriteLine("The service is running. To stop press enter.");
Console.ReadLine();
// Detach the input channel and stop listening.
// It releases the thread listening to messages.
myReceiver.DetachDuplexInputChannel();
}
// It is called when a message is received.
private static void OnMessageReceived(object sender,
TypedRequestReceivedEventArgs<MyRequest> e)
{
Console.WriteLine("Received: " + e.RequestMessage.Text);
// Create the response message.
MyResponse aResponse = new MyResponse();
aResponse.Length = e.RequestMessage.Text.Length;
// Send the response message back to the client.
myReceiver.SendResponseMessage(e.ResponseReceiverId, aResponse);
}
}
}