-
- 通信契约
- 以JSON体式格式实现信息传送
- 根蒂根基布局
- JSON数据转换
- 逻辑转换层
JAVA与.NET的彼此调用——TCP/IP彼此调用根蒂根基架构
添加时间:2013-6-2 点击量:TCP/IP套接字的概念
TCP/IP(传输把握和谈/网际和谈)是收集互连的通信和谈,经由过程它可以实现各类异构收集或异种机之间的互联通信。TCP/IP是Transmission Control Protocol/Internet Protocol的简写,中文译名为传输把握和谈/因特网互联和谈,又叫收集通信和谈,这个和谈是Internet最根蒂根基的和谈、Internet国际互联网络的根蒂根基,简单地说,就是由收集层的IP和谈和传输层的TCP和谈构成的。TCP/IP 定义了电子设备(比如策画机)如何连入因特网,以及数据如安在它们之间传输的标准。TCP/IP是一个四层的分层体系布局。高层为传输把握和谈,它负责凑集信息或把文件拆分成更小的包。低层是网际和谈,它处理惩罚每个包的地址项目组,使这些包正确的达到目标地。 TCP/IP已成为当今策画机收集最成熟、应用广泛的互联和谈。Internet采取的就是 TCP/IP和谈,收集上各类百般的策画机上只要安装了TCP/IP和谈,它们之间就能彼此通信。
TCP/IP套接字通信的开辟
在浩繁的开辟说话中,绝大项目组的开辟说话都支撑TCP/IP和谈通信,开辟过程也十分相像,先设置好Socket,然后由客户端发送恳求信息,办事器连接客户端接管到恳求后再返还信息。而在.NET体系傍边则稍有不合,体系把Socket对象包装在TcpClient对象内,对Socket对象的生命周期进行经管。在开辟过程傍边,办事器与客户端的开辟说话有所不合的景象经常产生,办事器是在JDK1.6的景象下进行开辟的,客户却请求应用.NET开辟客户端,这往往会令开辟人员感觉困惑!下面鄙人应用JAVA为办事器,.NET为客户端为例子,为大师介绍一下如何应用TCP/IP和谈进行JAVA .NET之间的彼此调用。像TCP/IP实现如许的例子很多,开创议来也斗劲简单,因为通信两边都是应用String来传送信息。而在真正建树ERP、OA、CRM等体系的时辰,通信两边都必须先建树一套同一的通信契约,才干实现TCP/IP通信,下面将为大师介绍一个斗劲典范的企业信息通信实例。
信息传送体式格式
因为.NET与JAVA各有不合的特点,两边不成能直接经由过程的序列化对象来传输信息,常用的信息互换体式格式有以下三种:
1. 最笨拙也是最错杂的一种传息体式格式,就是直接应用“头文件申明+字段属性”的体式格式。 这是一个既原始又麻烦的通信体式格式,因为每个契约都要以二进制的体式格式发送一个恳求,就算是同一类契约,跟着参数的不合,每个恳求的长度也会产生改变。如许的传息体式格式固然是麻烦,但在不合开辟说话彼此调用的时辰却经常会看到,这可能是因为开辟人员对两种开辟说话未能完全熟悉,所以倒置应用这最原始最简单的开辟体式格式。
2. 应用XML的信息传送体式格式,这是最常见,应用广泛的信息传递体式格式。在绝大多半的开辟平台都邑支撑XML,所以XML在Web收集传讯过程中常见。但XML大一个毛病就是过于堪舆,花费多量的传输流量。
3. 对于XML的毛病,JSON应运而生并且成长敏捷,JSON本是源于Javascript的,多半只用于B/S的页面开辟,但跟着技巧的成长和多个开辟说话的支撑,现今处处都可以看JSON的身影。因为JSON既供给一套跨平台的通信体式格式,也免除XML错杂特点,受到各类型开辟人员的迎接。
办事器端开辟
起首建树一套办事器与客户端同时接管通信契约, Contract 的name特点是契约的名称,办事器会经由过程此名称在Contracts.xml文件中找到该契约,然后按照output的package属性,class属性,method属性找到该契约的包名称,类名,调用的办法等属性。
Contracts.XML<Contracts>
<Contract name=GetPersonByAge> //name为契约名,办事器与客户端必须同时遵守此契约
<Input>
<Description>获取Age便是此值的People对象集</Description> //申明此契约内容
</Input>
<Output>
<Package>Manager</Package> //接管到GetPersonByAge恳求时所调用的包名称
<Class>PersonManager</Class> //接管到GetPersonByAge恳求时所调用的类名称
<Method>GetListByAge</Method> //接管到GetPersonByAge恳求时所调用的处理惩罚办法名称
</Output>
</Contract>
<Contract name=GetPersonByID>
<Input>
<Description>获取ID便是此值的People对象</Description>
</Input>
<Output>
<Package >Manager</Package>
<Class>PersonManager</Class>
<Method>GetListByID</Method>
</Output>
</Contract>
</Contracts>尽管今朝在C/S的开辟傍边大项目组还是应用序列化对象和分节字段的体式格式进行两边通信,但在这个实例傍边,鄙人想以JSON通信体式格式为例子来实现。起首,客户端会应用额定格局的JSON向办事器发送恳求:
{“ContractName”:“GetPeopleByAge”,“Params”:[23]}
ContractName代表着契约名称,体系会按照此名称在Contracts.xml文件中找到Name便是GetPeopleByAge的Contract项。然后在对应Output的子项Package,Class,Method中查找到对应的包,类型和办法。
Params是客户端传输过来的参数,办事器端会调用对象的办法输入参数23后,获得策画成果,最后把成果返还到客户端。
在这里有两点是值得重视的,第一点是JSON中的契约格局是固定的,办事器与客户端都必须遵守此契约,在ContractName中输入是必须对应的契约名称,而在Params中输入的必输是一个参数的凑集,哪怕里面只包含有一个参数。第二点是在Contracts.xml文件,Output里面的 Package,Class,Method是办事器端自定义的,它只是绑定了办事器端实现GetPersonByAge契约的办法,而这些办法并不是固定,办事器可以按照体系的须要而批改。这个做法有点像Struts里面的Struts.xml文件,其意义就是在于使办事器的处理惩罚办法与客户端发送的恳求实现分别。
体系的根蒂根基布局如图,客户端会以JSON体式格式{“ContractName”:“GetPeopleByAge”,“Params”:[23]}发送恳求到办事器,办事器会哄骗“数据转换层”把接管到的恳求转换成Contract对象。然后逻辑转换层会按照该Contract对象调用对应的办法,最后把策画成果以JSON体式格式返回到客户端。
重视在办事器与客户端信息互换的过程中,都是应用JSON格局。
在办事器端,当接到到客户端恳求后,Transfer类负责把接管到的JSON数据转换成Contract对象。在Transfer里面应用org.json对象包作为JSON的转化对象,org.json对象包可于以下网址http://www.json.org/java/index.html。
而Implement类包含GetResult(Contract contract )办法,其作就是按照contract对象Package,Class,Method等属性,调用“逻辑转换层”的对应办法,最后把策画成果返还给InputControl。
办事器端接管恳求后就会直接调用InputControl对输入的数据进行处理惩罚。
代码//Contract实体类,包含契约的package,class,method,params等多个属性
package Model;
import org.json.JSONArray;
publicclass Contract {
private String package1;
private String class1;
private String method;
private JSONArray params;
publicvoid setPackage1(String package1) {
this.package1 = package1;
}
public String getPackage1() {
return package1;
}
publicvoid setClass1(String class1) {
this.class1 = class1;
}
public String getClass1() {
return class1;
}
publicvoid setMethod(String method) {
this.method = method;
}
public String getMethod() {
return method;
}
publicvoid setParams(JSONArray params) {
this.params = params;
}
public JSONArray getParams() {
return params;
}
}
//把输入的String字符串转化为Contract对象
//在这里应用org.json对象包作为JSON的转化对象,org.json对象包可于以下网址http://www.json.org/java/index.html
package Common;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import Model.Contract;
import org.json.;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
publicclass Transfer {
private Transfer(){}
privatestatic String contractName=null;
privatestatic Contract contract=new Contract();
privatestatic JSONObject jsonObject=null;
publicstatic Contract GetContract(String data) throws Exception, JSONException, ParserConfigurationException, SAXException, IOException{
jsonObject=new JSONObject(data); //把字符串转化为JSONOject对象
GetContractName(); //获取契约名称
GetProperty(); //获取契约的package,class,method属性
GetParams(); //获取契约的参数集
return contract;
}
/
获取契约对应的包名称,类名称,办法名称
/
privatestaticvoid GetProperty() throws Exception{
File file =new File(Contracts.xml);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(file);
NodeList nodeList = doc.getElementsByTagName(Contract);
Element contractElement=null;
for (int i =0; i < nodeList.getLength(); i++) {
if(nodeList.item(i).getAttributes().item(0).getNodeValue().equals(contractName)){
contractElement=(Element)nodeList.item(i);
break;
}
}
if(contractElement!=null){
Element outputElement=(Element)contractElement.getElementsByTagName(Output).item(0);
contract.setPackage1(outputElement.getElementsByTagName(Package).item(0).getTextContent());
//获取包名称
contract.setClass1(outputElement.getElementsByTagName(Class).item(0).getTextContent());
//获取类名称
contract.setMethod(outputElement.getElementsByTagName(Method).item(0).getTextContent());
//获取办法名
}
else
thrownew Exception(未能找到对象的契约);
}
/
获取契约名称
/
privatestaticvoid GetContractName() throws JSONException{
contractName=jsonObject.getString(ContractName);
}
/
获取输入参数
/
privatestaticvoid GetParams() throws JSONException{
contract.setParams(jsonObject.getJSONArray(Params));
}
}
//调用Contract对象里面包中的类的某个办法,获取策画成果
package Common;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import Model.;
publicclass Implement {
private Contract contract;
private String fullName;
privatestatic Map<String,Object> objects=new HashMap<String,Object>(); //保存对象实体
privatestatic Map<String,Class> classes=new HashMap<String,Class>(); //保存类名
/
先获取对应的对象,再用反射模式调用对象的办法,获取策画成果
/
public Object GetResult(Contract contract){
this.contract=contract;
this.fullName=contract.getPackage1()+.+contract.getClass1();
try {
Object manager=GetObject();
Class theClass=classes.get(fullName);
Method method=theClass.getDeclaredMethod(contract.getMethod(),JSONArray.class);
return method.invoke(manager, contract.getParams());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
returnnull;
}
}
/
多次应用反射创建获取对象会损耗必然机能,所以此处应用单体模式获取对应的对象
/
private Object GetObject() throws InstantiationException, IllegalAccessException, ClassNotFoundException{
if(!objects.containsKey(fullName)){
Class theClass = Class.forName(fullName);
classes.put(fullName,theClass);
Object manager=theClass.newInstance();
objects.put(fullName, manager);
}
return objects.get(fullName);
}
}
//直接把接管到的二进制数据转换成String,然后经由过程Transfer把String转化为Contract对象,最后经由过程Implement获取运算成果
package Common;
import java.io.DataInputStream;
import Model.Contract;
publicclass InputControl {
private DataInputStream inputStream;
public InputControl(DataInputStream inputStream){
this.inputStream=inputStream;
}
/
直接把接管到的二进制数据转换成String,然后经由过程Transfer把String转化为Contract对象,最后经由过程Implement对象获取运算成果
/
public Object GetResult(){
byte[] byteMessage=newbyte[1024]; //在此处只获取测试数据,在真正运行时应应用分批缓存的体式格式
try{
int n=inputStream.read(byteMessage);
String message=new String(byteMessage,ASCII);
Contract contract=Transfer.GetContract(message);
Implement implement=new Implement();
Object result=implement.GetResult(contract);
return result;
}
catch(Exception ex){
ex.printStackTrace();
returnnull;
}
}
}
最后,体系经由过程OutputControl类把策画成果返还给客户端。
代码//把策画成果返回到客户端
package Common;
import java.io.DataOutputStream;
publicclass OutputControl {
private DataOutputStream outputStream;
public OutputControl(DataOutputStream outputStream){
this.outputStream=outputStream;
}
publicvoid Output(Object data){
try{
outputStream.writeBytes(data.toString());
outputStream.flush();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
//运行体系进行测试
package Common;
import java.io.;
import java.net.;
publicclass Program {
privatestatic ServerSocket serverSocket;
publicstaticvoid main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated method stub
Socket socket;
try {
serverSocket=new ServerSocket(5100); //激活5100端口
while(true){ //轮回捕获恳求
socket=serverSocket.accept();
DataOutputStream outStream=new DataOutputStream(socket.getOutputStream()); //获取DataOutputStream输出流
DataInputStream inputStream=new DataInputStream(socket.getInputStream()); //获取DataInputStream输入流
//调用InputControl对象获取运算成果
InputControl inputControl=new InputControl(inputStream);
Object result=inputControl.GetResult();
//调用OutputControl对象输入运算成果
OutputControl outputControl=new OutputControl(outStream);
outputControl.Output(result);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}如今先开辟一个例子作为参考,在完成客户端开辟的时辰就可以进行测试。这个例子是先在Manager包里面设置好一个类PersonManager,PersonManager类中包含一个名为GetListByAge的办法。在Contracts.xml文件设置一个名为GetPersonByAge的契约,客户端就可以经由过程这个契约在长途调用此办法获取策画成果。
代码//设置Person对象
package Model;
publicclass Person {
privateint id;
private String name;
privateint age;
publicvoid setId(int id) {
this.id = id;
}
publicint getId() {
return id;
}
publicvoid setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
publicvoid setAge(int age) {
this.age = age;
}
publicint getAge() {
return age;
}
}
//开辟PersonManager
package Manager;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import Model.;
publicclass PersonManager {
/
测试数据
/
private List<Person> GetList(){
List<Person> personList=new ArrayList<Person>();
Person person1=new Person();
person1.setId(0);
person1.setAge(23);
person1.setName(Mike);
personList.add(person1);
Person person2=new Person();
person2.setId(1);
person2.setAge(29);
person2.setName(Leslie);
personList.add(person2);
Person person3=new Person();
person3.setId(2);
person3.setAge(21);
person3.setName(Jack);
personList.add(person3);
Person person4=new Person();
person4.setId(3);
person4.setAge(23);
person4.setName(Rose);
personList.add(person4);
return personList;
}
/
获取春秋便是age参数的Person,因为数据将返还给客户端,所以这时把输出数据转化为JSONArray
/
public JSONArray GetListByAge(JSONArray jsonList) throws JSONException{
int age=jsonList.getInt(0); //因为输入参数为一个凑集params,所以即使只包含一个参数,也是经由过程要jsonList的第一个参数来获取的。
List<Person> personList=GetList();
List<Person> resultList=new ArrayList<Person>();
for(int n=0;n<personList.size();n++){
if(personList.get(n).getAge()==age)
resultList.add(personList.get(n));
}
JSONArray jsonArray=new JSONArray(resultList);
return jsonArray;
}
}然后在Contracts.xml设置绑定
XML<Contracts>
<Contract name=GetPersonByAge> //契约名称
<Input>
<Description>获取Age便是此值的People对象集</Description> //文字申明
</Input>
<Output>
<Package>Manager</Package> //绑定包
<Class>PersonManager</Class> //绑定类
<Method>GetListByAge</Method> //绑定处理惩罚办法
</Output>
</Contract>
</Contracts>绑定今后,在完成客户端开辟的时辰就可以进行测试。应用这开辟模式的益处在于哄骗JSON感化数据传输的桥梁,解决不合开辟平台之间数据难以同步的题目。应用JSON比XML更轻易操纵,可以削减传输流量,并且受到各开辟说话的支撑。应用Contracts.xml在办事器绑定处理惩罚体式格式,使办事器的处理惩罚办法与客户端发送的恳求实现分别。下面开端介绍一下客户端的开辟。
客户端开辟
客户端的开辟的开辟相对简单,因为契约是应用 {“ContractName”:“GetPeopleByAge”,“Params”:[23]} JSON体式格式进行传送,所以先开辟一个MessageEntity实体类来承载契约。
代码namespace Model
{
[DataContract]
publicclass MessageEntity
{
//契约名称
[DataMember]
publicstring ContractName
{
get;
set;
}
//重视参数应用凑集的体式格式来传送
[DataMember]
public IList<Object> Params
{
get;
set;
}
}
}然后开辟一个MessageManager信息经管器来经管契约的传送过程,因为Framework4.0里面,未能对JSON数据中凑集的转换供给一个简单函数,所以在MessageManager里面应用了一个Newtonsoft.Json对象包,该对象包里面对JSON的操纵有着强大支撑,可以在http://www.codeplex.com/官方网站
代码using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Threading;
using Model;
using Newtonsoft.Json;
namespace Common
{
publicclass MessageManager
{
privatestatic TcpClient _tcpClient;
//设置tcpClient对象
publicstatic TcpClient TcpClient
{
set { _tcpClient = value; }
}
//此处只应用静态办法实现数据传送,发送恳求后应用Thread.Sleep守候运算成果,如许存在必然风险,也会降落效力
//在大型的开辟傍边应当进一步改良,把信息发送与信息接管分隔处理惩罚
publicstaticobject GetMessage(MessageEntity message, Type type)
{
NetworkStream networkStream = _tcpClient.GetStream();
//哄骗DataContractJsonSerializer将MessageEntity对象实现序列化,发送到办事器
DataContractJsonSerializer jsonSerializer =new DataContractJsonSerializer(typeof(MessageEntity));
lock (networkStream)
{
jsonSerializer.WriteObject(networkStream, message);
networkStream.Flush();
}
Thread.Sleep(500);
//获取回传信息,这里设置接管值1024个字节
//在实际的开辟傍边应当应用分批缓存的体式格式实现数据接管
byte[] messageByte =newbyte[1024];
int n =0;
lock (networkStream)
n = networkStream.Read(messageByte, 0, 1024);
if (n ==0)
returnnull;
//按照输入的type对象,把二进制信息转化为对应的对象
string jsonMessage = Encoding.ASCII.GetString(messageByte);
//哄骗Netonsoft.Json对象集将获取的JSON数据转化对象
object returnValue = JavaScriptConvert.DeserializeObject(jsonMessage, type);
return returnValue;
}
}
}
下面开辟一个GetPersonByAge 契约作为例子
代码using System;
using System.Collections.Generic;
using System.Text;
using Model;
using Common;
namespace DAL
{
publicclass PersonDAL
{
///<summary>
/// 建树MessageEntity对象,重视输入额定契约名称及数据参数,获取查询成果
///</summary>
///<param name=age>Person的春秋</param>
///<returns>获取春秋便是此值的Person对象集</returns>
public IList<Person> GetPersonByAge(int age)
{
//先建树一个MessageEntity对象,设定其ContractName及参数凑集
//重视ContractName的值必须与办事器端Contracts.xml文件中Contract 项的 name 特点相对应
MessageEntity messageEntity =new MessageEntity();
messageEntity.ContractName =GetPersonByAge;
messageEntity.Params =new List<Object> { age };
//调用MessageManager的GetMessage办法获取策画成果
IList<Person> personList = (List<Person>)MessageManager.GetMessage(messageEntity, typeof(List<Person>));
return personList;
}
}
}PersonDAL类中的GetPersonByAge办法就是把契约封装在MessageEntity傍边,再哄骗MessageManager把契约发送到办事器端获取运行成果,然后把成果转换为JSON,最后哄骗Netonsoft.Json对象集的JavaScriptConvert类,把JSON转换成Person对象。
测试
代码Person实编制
namespace Model
{
publicclass Person
{
privateint _id;
privatestring _name;
privateint _age;
publicint id
{
get { return _id; }
set { _id = value; }
}
publicint age
{
get { return _age; }
set { _age = value; }
}
publicstring name
{
get { return _name; }
set { _name = value; }
}
}
}
直接调用DAL层
namespace BLL
{
publicclass PersonBLL
{
private PersonDAL personDal;
public PersonBLL()
{
personDal =new PersonDAL();
}
public IList<Person> GetPersonByAge(int age)
{
IList<Person> personList=personDal.GetPersonByAge(age);
if (personList.Count !=0)
return personList;
else
returnnew List<Person>();
}
}
}
测试
class Program
{
privatestatic TcpClient tcpClient =new TcpClient();
staticvoid Main(string[] args)
{
tcpClient.Connect(127.0.0.1, 5100);
MessageManager.TcpClient = tcpClient;
PersonBLL personBll =new PersonBLL();
IList<Person> personList=personBll.GetPersonByAge(23);
if (personList.Count !=0)
Console.WriteLine(personList.Count.ToString());
Console.ReadKey();
}
}
重视测试是输入的查询前提转换成JSON后是 {“ContractName”:“GetPeopleByAge”,“Params”:[23]},而这种 “ContractName: 契约名,Params: {参数,参数,...} 传送格局是固定不成改变的。当获取查询成果 [{\id\:0,\age\:23,\name\:\Mike\},{\id\:3,\age\:23,\name\:\Rose\}] 后 ,MessageManager将经由过程Newtonsoft.Json把返还值转换为List<Person>。
到此处,鄙人为大师介绍了哄骗JSON数据实现JAVA与.NET之间TCP/IP彼此调用,其实以JSON的体式格式实现并不是独一的选择,只是鄙人想在惯常的用法之上,哄骗一下这个另类的办法,至于在开辟布局上有不敷周全的处所敬请各位点评。至于以.NET为办事器,JAVA为客户端的TCP/IP通信实例与此例子极为相像,在此就不作介绍了。
- 通信契约