使用thrift做项目的时候,用到了thrift框架,后来遇到一个很棘手的问题,就是在使用TBinaryProtocol TFramedTransport TNonblockingServerSocket等协议时,服务器端无法得到client的ip地址。
经过了几天的研究,发现如下方法可以极简(三行)更改代码的同时,解决获取ip的问题。
服务器代码:
public static void main(String[] args) {
try {
TNonblockingServerSocket socket = new TNonblockingServerSocket(PORT);
TProcessor processor = new KkLog.Processor(new KkServiceImpl());
TThreadedSelectorServer.Args arg = new TThreadedSelectorServer.Args(socket);
arg.transportFactory(new TFramedTransport.Factory());
arg.protocolFactory(new TBinaryProtocol.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new TThreadedSelectorServer(arg);
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
客户端代码:
public static void main(String[] args) {
if(args.length<1){
System.out.println("run as java -jar KkClient.jar serverIP");
return ;
}
TTransport transport = new TFramedTransport(new TSocket(IP, PORT, clientTimeout));
TProtocol protocol = new TBinaryProtocol(transport);
KkLog.Client client = new KkLog.Client(protocol);
try {
transport.open();
client.sendLog("0.0.0.0", data); //这里客户端调用sendLog发送data,前面是ip地址,由服务器接收到后填充。
} catch (TApplicationException e) { // 异常的文档 http://people.apache.org/~thejas/thrift-0.9/javadoc/org/apache/thrift/TException.html
System.out.println(e.getMessage() + " " + e.getType());
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
transport.close();
}
从maven下载了thrift-0.9.3版本的源码,修改地方如下:
修改org.apache.thrift.protocol.TBinaryProtocol.java文件,增加一行代码:public String client_ip="0.0.0.0";
思想是将获取到ip后,将ip保存到client_ip中,需要用到时再提取即可。
package org.apache.thrift.protocol;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;
/**
* Binary protocol implementation for thrift.
* Customized by tongange@haizhi.com 20160830
*/
public class TBinaryProtocol extends TProtocol {
private static final TStruct ANONYMOUS_STRUCT = new TStruct();
private static final long NO_LENGTH_LIMIT = -1;
protected static final int VERSION_MASK = 0xffff0000;
protected static final int VERSION_1 = 0x80010000;
public String client_ip="0.0.0.0"; //《《《《《《《《《《—————————在这里增加
/**
* The maximum number of bytes to read from the transport for
* variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for
* unlimited.
*/
private final long stringLengthLimit_;
接下来修改AbstractNonblockingServer.java 文件在这里将client_ip 写入:一行代码
public void invoke() {
frameTrans_.reset(buffer_.array());
response_.reset();
try {
if (eventHandler_ != null) {
eventHandler_.processContext(context_, inTrans_, outTrans_);
}
//下面这行是从TNonblockingSocket中获取socketChannel进而获取client 的ip。并保存到inProt_的新加字段中。下面的强制转换根据自己实际用到的协议实际填写。((TBinaryProtocol)inProt_).client_ip=((InetSocketAddress)((TNonblockingSocket)this.trans_).getSocketChannel().getRemoteAddress()).getAddress().getHostAddress();
processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_);
responseReady();
return;
} catch (TException te) {
LOGGER.warn("Exception while invoking!", te);
} catch (Throwable t) {
LOGGER.error("Unexpected throwable while invoking!", t);
}
// This will only be reached when there is a throwable.
state_ = FrameBufferState.AWAITING_CLOSE;
requestSelectInterestChange();
}
这样,我们就将client的ip保存下来。
下面修改thrift生成的servive 文件中增加一行代码:
private static class sendLog_argsStandardScheme extends StandardScheme<sendLog_args> {
public void read(org.apache.thrift.protocol.TProtocol iprot, sendLog_args struct) throws org.apache.thrift.TException {
org.apache.thrift.protocol.TField schemeField;
iprot.readStructBegin();
while (true)
{
schemeField = iprot.readFieldBegin();
if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
break;
}
switch (schemeField.id) {
case 1: // IP
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.ip = iprot.readString();
struct.ip = ((TBinaryProtocol)iprot).client_ip; //在这里添加使用我们保存的ip覆盖客户端传来的。
//因为客户端无法获得自己的ip,所以客户端发来什么都无所谓,保留接口在服务器端填充就行。
struct.setTypeIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // LOG
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.log = iprot.readString();
struct.setLogIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
default:
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
struct.validate();
}
public void write(org.apache.thrift.protocol.TProtocol oprot, sendLog_args struct) throws org.apache.thrift.TException {
struct.validate();
oprot.writeStructBegin(STRUCT_DESC);
if (struct.type != null) {
oprot.writeFieldBegin(TYPE_FIELD_DESC);
oprot.writeString(struct.type);
oprot.writeFieldEnd();
}
if (struct.log != null) {
oprot.writeFieldBegin(LOG_FIELD_DESC);
oprot.writeString(struct.log);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
}
生文件是执行 thrift --gen java **.thrift之后生成的,sendLog_args是sendLog方法的参数,也可以调试根据数据流向来定位此代码。
这样我们将client_ip取出来就完成了。
上述更改需要首先系在thrift源码包: libthrift-0.9.3-sources.jar。修改代码后再本地编译生成jar包,在使用工程导入即可。
注:一定要注意自己使用的协议是否和我的相同,如果不同需要相应修改。
欢迎转载,请注明出处。http://blog.youkuaiyun.com/ttttaaaagggg/article/details/52385750
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1lqzj1jd8n7e4