Search code examples
rubymultithreadingasynchronoustcpsocket

One TCPSocket, two threads


I need to implement a listening loop for TCPSoket client which will not block sending data to the same socket.

The approximate structure is so:

class SocketWrapper
  def initialize
    @messages = []
    @socket = TCPSocket.open('192.168.0.25', 2000)
    Thread.new do
       loop do
          @messages.push @socket.read
       end
    end
  end

  def send_message(msg)
    @socket.write msg
  end

  def read_messages
    @messages.dup
    @messages.clear
  def
end

Is it everything ok with this structure?

I need to implement two strategies here:

  1. response = send_request(request_data)
  2. listener - when incoming data is not a response for my request, but asynchronous data which I have to listen to.

Solution

  • Ruby has a class especially designed for that - Queue:

    class SocketWrapper
      def self.instance
        @inst ||= self.new
      end
    
      def initialize
        @messages = Queue.new
        @socket = TCPSocket.open('192.168.0.25', 2000)
        Thread.new do
           loop do
              @messages << @parse_message.nil? ? @socket.read : @parse_message.call(@socket.read)
           end
        end
      end
    
      def parse_message(&block)
        @parse_message = block
      end
    
      def send_message(msg)
        @socket.write msg
      end
    
      def read_messages
        result = []
        begin
          while message = @messages.pop(true)
            result << message
          end
        rescue ThreadError
          #raised if queue is empty
        end
        result
      def
    end
    

    In initializers:

    SocketWrapper.instance.parse_message do |raw|
      JSON.parse(raw)
      # or any other awesome thing with raw
    end
    

    In controller:

    SocketWrapper.instance.send_message('blah blah blah')