TTransportException when using TFramedTransport
java, thrift
Solution
I was having the same problem and finally found the answer. It is possible to set the transport type on the server, though this is not clear from most tutorials and examples I've found on the web. Have a look at all of the methods of the `TServer.Args` class (or the args classes for other servers, which extend `TServer.Args`). There are methods `inputTransportFactory` and `outputTransportFactory`. You can use `new TFramedTransport.Factory()` as inputs to each of these methods to declare which transport the server should use. In scala:
val handler = new ServiceStatusHandler
val processor = new ServiceStatus.Processor(handler)
val serverTransport = new TServerSocket(9090)
val args = new TServer.Args(serverTransport)
.processor(processor)
.inputTransportFactory(new TFramedTransport.Factory)
.outputTransportFactory(new TFramedTransport.Factory)
val server = new TSimpleServer(args)
println("Starting the simple server...")
server.serve()
Note that if you are using a `TAsyncClient`, you have no choice about the transport that you use. You must use `TNonblockingTransport`, which has only one standard implementation, `TNonblockingSocket`, which internally wraps whatever protocol you are using in a framed transport. It doesn't actually wrap your chosen protocol in a `TFramedTransport`, but it does prepend the length of the frame to the content that it writes, and expects the server to prepend the length of the response as well. This wasn't documented anywhere I found, but if you look at the source code and experiment with different combinations, you will find that with `TSimpleServer` you must use `TFramedTransport` to get it to work with an async client.
By the way, it's also worth noting that the docs say that a `TNonblockingServer` must use `TFramedTransport` in the outermost later of the transport. However, the examples don't show this being set in `TNonblockingServer.Args`, yet you still find that you must use `TFramedTransport` on the client side to successfully execute an rpc on the server. This is because `TNonblockingServer.Args` has its input and output protocols set to `TFramedTransport` by default (you can see this using reflection to inspect the fields of the superclass hierarchy or in the source code for the constructor of `AbstractNonblockingServerArgs` -- you can override the input and output transports, but the server will likely fail for the reasons discussed in the documentation).
Problem
I'm pretty puzzled with this issue. I have an Apache Thrift 0.9.0 client and server. The client code goes like this: ``` this.transport = new TSocket(this.server, this.port); final TProtocol protocol = new TBinaryProtocol(this.transport); this.client = new ZKProtoService.Client(protocol); ``` This works fine. However, if I try to wrap the transport in a `TFramedTransport` ``` this.transport = new TSocket(this.server, this.port); final TProtocol protocol = new TBinaryProtocol(new TFramedTransport(this.transport)); this.client = new ZKProtoService.Client(protocol); ``` I get the following obscure (no explanation message whatsoever) exception in the client side. Server side shows no error. ``` org.apache.thrift.transport.TTransportException at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:132) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:84) at org.apache.thrift.transport.TFramedTransport.readFrame(TFramedTransport.java:129) at org.apache.thrift.transport.TFramedTransport.read(TFramedTransport.java:101) at org.apache.thrift.transport.TTransport.readAll(TTransport.java:84) at org.apache.thrift.protocol.TBinaryProtocol.readAll(TBinaryProtocol.java:378) at org.apache.thrift.protocol.TBinaryProtocol.readI32(TBinaryProtocol.java:297) at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:204) at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:69) at com.blablabla.android.core.device.proto.ProtoService$Client.recv_open(ProtoService.java:108) at com.blablabla.android.core.device.proto.ProtoService$Client.open(ProtoService.java:95) at com.blablabla.simpleprotoclient.proto.ProtoClient.initializeCommunication(ProtoClient.java:411) at com.blablabla.simpleprotoclient.proto.ProtoClient.doWork(ProtoClient.java:269) at com.blablabla.simpleprotoclient.proto.ProtoClient.run(ProtoClient.java:499) at java.lang.Thread.run(Thread.java:724) ``` It also fails if I use `TCompactProtocol` instead of `TBinaryProtocol`. In the server side I have extended `TProcessor` with my own class since I need to reuse existing service handler (the service server-side `IFace` implementation) for this client: ``` @Override public boolean process(final TProtocol in, final TProtocol out) throws TException { final TTransport t = in.getTransport(); final TSocket socket = (TSocket) t; socket.setTimeout(ProtoServer.SOCKET_TIMEOUT); final String clientAddress = socket.getSocket().getInetAddress() .getHostAddress(); final int clientPort = socket.getSocket().getPort(); final String clientRemote = clientAddress + ":" + clientPort; ProtoService.Processor<ProtoServiceHandler> processor = PROCESSORS .get(clientRemote); if (processor == null) { final ProtoServiceHandler handler = new ProtoServiceHandler( clientRemote); processor = new ProtoService.Processor<ProtoServiceHandler>( handler); PROCESSORS.put(clientRemote, processor); HANDLERS.put(clientRemote, handler); ProtoClientConnectionChecker.addNewConnection(clientRemote, socket); } return processor.process(in, out); } ``` And this is how I start the server side: ``` TServerTransport serverTransport = new TServerSocket(DEFAULT_CONTROL_PORT); TServer server = new TThreadPoolServer(new TThreadPoolServer.Args( serverTransport).processor(new ControlProcessor())); Thread thControlServer = new Thread(new StartServer("Control", server)); thControlServer.start(); ``` I have some questions: - Is it correct to reuse service handler instances or I shouldn't be doing this? - Why does it fail when I use `TFramedTransport` or `TCompactProtocol`? How to fix this? Any help on this issue is welcome. Thanks in advance!