经过一番研究后,摸清楚了thrift的基本使用方法和其工作原理.thrift的确是封装了java原生Socket,所以从根本上来讲是可以实现双向通信的,正如上一篇文章中翻译的外文中所说的,无需建立双连接和轮询,只需要做很少量的工作这个目的就可以达到先谈谈thrift的使用步骤,首先我们需要根据thrift规定的语法来书写一个.thrift文档,比如:
namespace java com.zyt.student
struct Student {
1: string name;
}
service Zthrift {
oneway void send(Student msg)
}
这个文档将来会被用来生成用来调用的.java文件.正如官网上所说,我们需要下载一个thrift编译器,用来将这个文档编译成我们能看懂的.java文件.具体怎么做就不赘述了,官网都有,属于基础知识.编译后我们将得到一个ZThrift.java文件.
然后就是调用过程,首先在客户端,我们需要一个TSocket(使用起来和原生Socket很类似),只是thrift隐藏了许多细节,引入了thrift独有的数据传输协议(或者理解为格式).然后使用上面生成的文件中的一个叫做Client的内部类来作为我们发送数据的类,这个类中的send方法对应上面我们写的(带有返回值的方法在双向通信时会报错,具体原因可能是由于内存溢出造成的,毕竟双方都变成了回复方,无休止的来回发送对虚拟机来说是致命的).通常我们在使用thrift的时候,一般步骤是先初始化协议,然后创建ZThrift.Client的对象,然后开启连接:TSocket.open(),然后使用client的send方法去发送数据.但是我们要实现双向通讯,就必须既有发送程序,又有接收程序,这就要求必须再写一段程序来接收服务端返回的数据.幸运的是,在我们上面的ZThrift类中,有个叫做Processor的类,它继承自TBaseProcessor类,这个类有一个”阻塞式”方法可以用来监控服务端是否发回数据:process(TProticol in,TProtocol out).只要将我们客户端的TSocket传入TProtocol的构造函数里面,就可以起到监控作用.而且由于这个方法是阻塞式的,我们有必要新开启一个线程来执行这个工作.
客户端的工作量其实是很少的,基本上就是在原有的基础上新加了一个线程来接收服务器返回的数据而已.而服务端就有点坑爹了,为什么呢?因为一台电脑要与服务器通信,只需要知道服务器的ip即可,但是反过来就没那么容易了,我们的个人电脑和各种终端往往是置于内网当中,这使得通信变得艰难起来(这就是NAT端口映射技术出现的原因).但是会编写程序的人都知道,使用Socket就可以实现双向通信,即只需要一个来自客户端的Socket,我们就可以打开通往客户端的道路.因此我们要做的,就是在服务端拦截到这个来自客户端的Socket.通过研究发现,TSocket底层是封装了Socket的,因此要拦截Socket就可以从TSocket来下手,从上一期的文章中已经得知,Thrift提供了一个叫做TProcessorFactory的类,这个类可以用来处理客户端发来的数据,其中有个方法叫做getProcessor(TTransport),它给我们提供了拦截Socket的机会,为什么呢,因为TSocket的父类的父类就是这个TTransport,也就是说,这个TTransport是来自于客户端的.那么接下来,我们可以将这个TTransport拦截下来,用来构建一个ZThrift.Client的对象,然后我们就可以使用这个Client来给客户端回消息了.接下来怎么做,相信大家也明白了吧,下面贴上源码:
客户端:
package test;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransportException;
import com.thrift.common.ZThrift;
import com.thrift.common.ZThrift.Iface;
import com.thrift.common.Student;
public class ZClient {
public static void main(String[]args){
TSocket tSocket=new TSocket("192.168.1.173",9999);
ZThrift.Client client=new ZThrift.Client(new TBinaryProtocol(tSocket));
try {
tSocket.open();
new Thread(new Runnable() {
@Override
public void run() {
ZThrift.Processor<Iface> mp=new ZThrift.Processor<Iface>(new Iface() {
@Override
public void send(Student msg)
throws TException {
System.out.println("收到消息:"+msg);
}
});
try {
while(mp.process(new TBinaryProtocol(tSocket), new TBinaryProtocol(tSocket) )){
//阻塞式方法,不需要内容
}
} catch (TException e) {
System.out.println("连接已断开...");
e.printStackTrace();
}
}
}).start();
client.send(new Student("小明"));
} catch (TTransportException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
然后是服务端:
package test;
import org.apache.thrift.TException;
import org.apache.thrift.TProcessor;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransportException;
import com.thrift.common.ZThrift;
import com.thrift.common.ZThrift.Iface;
import com.thrift.common.Student;
public class ZServer {
public static void main(String[]args){
try {
TServerSocket tServerSocket=new TServerSocket(9999);
TThreadPoolServer.Args a=new TThreadPoolServer.Args(tServerSocket);
TBinaryProtocol.Factory factory=new TBinaryProtocol.Factory();
TProcessorFactory tProcessorFactory=new TProcessorFactory(null){
public TProcessor getProcessor(org.apache.thrift.transport.TTransport tTransport) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);//延时五秒回复
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ZThrift.Client client=new ZThrift.Client(new TBinaryProtocol(tTransport));//这里可以把client提取作为成员变量来多次使用
try {
client.send(new Student("小红"));
} catch (TException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
return new ZThrift.Processor<Iface>(new Iface() {
@Override
public void send(Student msg)
throws TException {
System.out.println(msg.toString());
}
});
};
};
a.protocolFactory(factory);
a.processorFactory(tProcessorFactory);
TThreadPoolServer tThreadPoolServer=new TThreadPoolServer(a);
System.out.println("start server...");
tTheadPoolServer.serve();
} catch (TTransportException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
以上代码供大家参考.