Wednesday, March 6, 2013

Communication between Python and Ruby Using Sockets

Sockets are a neat and (relatively) simple means of communication between different programs in a client-server relationship. They can also be used when it is necessary to communicate data between languages and speed is not a large issue.

The following code examples are a Python lyric server that I built and a Ruby client. Their full code is available on my github account.

simple_lyric_server.py (some implementation details hidden, see github for more):

import socket

 class LyricServer(object):
    def __init__(self):
        # Details hidden... See github

    def try_get_lyrics(self):
       
# Details hidden... See github

    def define_socket(self, port, n_requests=5):
        """
            Used to define the socket object from inputs
        """
        host = socket.gethostname()
        print "Lyric Thief Server: Hostname is %s" % str(host)
        self._socket.bind((host, port))
        self._n_requests = n_requests

    def handle_lyric_requests(self):
        self._socket.listen(self._n_requests)

        while True:
            client, address = self._socket.accept()
            print "Lyric Thief Server: Connected to %s" % str(address)

            self.lyrics = ''
            data = json.loads(client.recv(1024))
            try:
                self.artist, self.song = data['artist'], data['song']
            except KeyError:
                client.send("Invalid JSON passed")
                print "Lyric Thief Server: Invalid JSON Passed"
                client.close()
                return

            self.try_get_lyrics()

            # Try sending the lyrics line-by line

            for line in self.lyrics.split('\n'):
                client.send(line+'\n')

            print "Lyric Thief Server: Sent Lyrics, Closing Connection"
            client.close()

 

if __name__ == '__main__':
    server = LyricServer()
    server.define_socket(915, 5)
    server.handle_lyric_requests() 



So, quickly walking through what the methods do:
  • define_socket(self, port, n_requests=5) uses the properties attached to a socket object to construct the server's socket, where port is the port that the server will run on and n_requests specifies the maximum number of requests that the server can handle at once.
  • handle_lyric_request(self) tries to lookup (online) the lyrics of the song/artist pair JSON data that is passed in from a client and then send back an appropriate response.

Now looking at ruby_client.rb: 
require 'socket'

host = Socket.gethostname
port = 915

s = TCPSocket.new host, port
s.puts('{ "artist" : "Counting Crows", "song" : "Omaha"}')

lyrics = ""

begin
  while line = s.gets
    lyrics += line

  end
rescue
end 
s.close 

So, this Ruby client is much simpler. Essentially all it's doing is (1) building a new TCPSocket using the host machine and the same port as specified in the Python server, (2) writing a JSON object to the port (a request for the "Counting Crows" song "Omaha"), then it (3) reads in the lyrics and closes the connection.

2 comments: