1

I am trying to learn how Thrift(version 0.9.2) works in python with a simple example. The server code works fine, but running the client code gives the error Could not connect to localhost:9090, and I tried the shell commands

netstat -nap | grep 9090, this outputs

tcp 0 0 0.0.35.130:9090 0.0.0.0:* LISTEN 4656/python,

and command

nc -zv localhost 9090 which outputs

nc: connect to localhost port 9090 (tcp) failed: Connection refused.

At this point I am not sure which part (the computer itself, Thrift, or the code?) has gone wrong. All the code is given as following, would anyone point out where the mistake is?

Below is the helloworld.thrift:

const string HELLO_IN_KOREAN = "an-nyoung-ha-se-yo"
const string HELLO_IN_FRENCH = "bonjour!"
const string HELLO_IN_JAPANESE = "konichiwa!" 
service HelloWorld {
  void ping(),
  i32 sayHello(),
  i32 sayMsg(1:string msg)
}

and the server code is,

import sys
sys.path.append('../gen-py')

from helloworld import HelloWorld
from helloworld.ttypes import *

from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

import socket

class HelloWorldHandler:
  def __init__(self):
    self.log = {}

  def ping(self):
    print "ping()"

  def sayHello(self):
    print "sayHello()"
    return "say hello from " + socket.gethostbyname()

  def sayMsg(self, msg):
    print "sayMsg(" + msg + ")"
    return "say " + msg + " from " + socket.gethostbyname()

handler = HelloWorldHandler()
processor = HelloWorld.Processor(handler)
transport = TSocket.TServerSocket('9090')
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()

server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)

print "Starting python server..."
server.serve()
print "done!"

and this is the client,

import sys
sys.path.append('../gen-py')

from helloworld import HelloWorld
from helloworld.ttypes import *
from helloworld.constants import *

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

try:
  # Make socket
  transport = TSocket.TSocket('localhost', 9090)

  # Buffering is critical. Raw sockets are very slow
  transport = TTransport.TBufferedTransport(transport)

  # Wrap in a protocol
  protocol = TBinaryProtocol.TBinaryProtocol(transport)

  # Create a client to use the protocol encoder
  client = HelloWorld.Client(protocol)

  # Connect!
  transport.open()

  client.ping()
  print "ping()"

  msg = client.sayHello()
  print msg
  msg = client.sayMsg(HELLO_IN_KOREAN)
  print msg

  transport.close()

except Thrift.TException, tx:
  print "%s" % (tx.message)
JensG
  • 13,148
  • 4
  • 45
  • 55
Allanqunzi
  • 3,230
  • 1
  • 26
  • 58
  • What I can tell from the error message, there's probably something wrong outside of Thrift. Did you look at [this very similar question](http://stackoverflow.com/questions/23494136/connection-refused-connect2-ruby-on-rails-mail-setup)? – JensG Jun 23 '15 at 17:56

1 Answers1

4

I think the problem is with your call -

transport = TSocket.TServerSocket('9090')

This should be

transport = TSocket.TServerSocket(host='localhost', port=9090) 

or

transport = TSocket.TServerSocket(port=9090)

Actually - port argument is not even required. If you don't give, it's default is 9090. Your code is saying host is 9090

This can be clear from the netstat -nap output. The line indeed shows 'something' listening on port 9090 (that is because the default port in thrift TServerSocket is 9090), but check the listening address, it is 0.0.35.130. This should be 0.0.0.0 for any interface or 127.0.0.1 for localhost.

Edit:

In fact if you do a socket.getaddrinfo('9090', 9090). It does indeed show an address of 0.0.35.130, so what you see in netstat is not surprising.

Your nc output is also saying nothing is listening on localhost:9090 (by the Connection Refused Error).

The above fix should fix the problem.

gabhijit
  • 3,345
  • 2
  • 23
  • 36
  • I checked that `TSocket.TServerSocke`'s `__init__` constructor defaults `host` as `None`, so changing to `(host='localhost', port=9090)` fixed the problem. But it's strange that in the [official thrift python tutorial](https://thrift.apache.org/tutorial/py) in the server code it doesn't specify `host='localhost'` for `TSocket.TServerSocket`, and this official code works fine on my machine !! And I have checked that both the official code and the code posted in this question call the same `__init__` constructor of `TSocket.TServerSocket`. Any clue why there is such a difference? – Allanqunzi Jun 24 '15 at 20:32
  • In fact I quickly checked it is not the same that you are calling. what you are calling is `TSocket.TServerSocket('9090') ` and what the officiak document is calling is `transport = TSocket.TServerSocket(port=9090)`. `host='localhost'` is not explicitly required, if `host` is `None`, in `socket.getaddrinfo(None, 9090)` resolves to `localhost`. So default `None` is okay. – gabhijit Jun 25 '15 at 01:48
  • Thank you very much! I got it now. I just noticed that there was another mistake that in the server the return values of `sayHello` and `sayMsg` are not the same as those defined in `helloworld.thrift`. – Allanqunzi Jun 25 '15 at 04:09