module GxG::Support::Library::SocketIO

Public Instance Methods

recv(bytecount=nil, flags=0) click to toggle source
# File lib/gxg/gxg_io.rb, line 340
def recv(bytecount=nil, flags=0)
  # for now, flags ignored.  LATER: study Socket.recv message flags in detail
  unless self.instance_variable_defined?(:@before_receive_data)
    @before_receive_data = nil
  end
  unless self.instance_variable_defined?(:@after_receive_data)
    @after_receive_data = nil
  end
  unless bytecount.is_a?(::Numeric)
    bytecount = self.buffer_limits(:in,:socket)[:initial]
  end
  unless flags.is_a?(::Numeric)
    flags = 0
  end
  if self.fcntl(::Fcntl::F_GETFL,::Fcntl::O_NONBLOCK) & ::Fcntl::O_NONBLOCK == ::Fcntl::O_NONBLOCK
    # on Winderz, just to a buffer sized spoon full until done ??
    #
    if self.closed?()
      raise IOError, "attempting to recv on a closed stream"
    else
      if @before_receive_data.respond_to?(:call)
        @before_receive_data.call()
      end
      if self.is_any?(::UNIXServer, ::UNIXSocket, ::Socket)
        whatfor = :socket
      else
        if self.is_any?(::TCPServer, ::SOCKSocket, ::TCPSocket)
          whatfor = :tcp
        else
          if self.is_any?(::UDPServer, ::UDPSource, ::UDPSocket)
            whatfor = :udp
          end
        end
      end
      buffer_size = self.buffer_limits(:in,whatfor)[:initial]
      unless bytecount.is_a?(::Numeric)
        bytecount = buffer_size
      end
      unless flags.is_a?(::Numeric)
        flags = 0
      end
      # millisecond: 1/1000th of a second (reminder)
      read_bytes = 0
      result = ""
      result.force_encoding(::Encoding::ASCII_8BIT)
      chunk = "abc"
      #
      until (read_bytes == bytecount || chunk.bytesize() == 0)
        begin
          if ((bytecount - read_bytes) < buffer_size)
            chunk = self.recv_nonblock((bytecount - read_bytes),flags).to_s
          else
            chunk = self.recv_nonblock(buffer_size,flags).to_s
          end
          read_bytes += chunk.bytesize()
          if chunk.bytesize() > 0
            result << chunk
          end
          #
        rescue ::IO::WaitReadable, ::IO::WaitWritable, GxG::IO::IO::WaitReadable, GxG::IO::IO::WaitWritable
          pause
          begin
            # about 10 ms
            selected = GxG::IO::IO::select([self],nil,nil,0.010)
            until selected
              pause
              selected = GxG::IO::IO::select([self],nil,nil,0.010)
            end
          rescue Exception
            pause
            retry
          end
          retry
        end
        #
        pause
      end
      #
      if @after_receive_data.respond_to?(:call)
        @after_receive_data.call([result,nil,0,nil])
      end
      result
    end
  else
    if self.closed?()
      raise IOError, "attempting to recv on a closed stream"
    else
      if @before_receive_data.respond_to?(:call)
        @before_receive_data.call()
      end
      result = self.original_recv(bytecount.to_i,flags).to_s
      result.force_encoding(::Encoding::ASCII_8BIT)
      if @after_receive_data.respond_to?(:call)
        @after_receive_data.call([result,nil,0,nil])
      end
    end
  end
  result
end
recvmsg(max_mesg_length=nil, flags=0, max_control_length=nil, options={}) click to toggle source
# File lib/gxg/gxg_io.rb, line 281
def recvmsg(max_mesg_length=nil, flags=0, max_control_length=nil, options={})
  # for now, flags ignored.  LATER: study Socket.recv message flags in detail
  if self.fcntl(::Fcntl::F_GETFL,::Fcntl::O_NONBLOCK) & ::Fcntl::O_NONBLOCK == ::Fcntl::O_NONBLOCK
    # on Winderz, just to a buffer sized spoon full until done ??
    if self.closed?()
      raise IOError, "attempting to recvmsg on a closed stream"
    else
      if @before_receive_data.respond_to?(:call)
        @before_receive_data.call()
      end
      begin
        result = self.recvmsg_nonblock(max_mesg_length, flags, max_control_length, options)
        #
      rescue ::IO::WaitReadable, ::IO::WaitWritable, GxG::IO::IO::WaitReadable, GxG::IO::IO::WaitWritable
        pause
        begin
          # about 10 ms
          selected = GxG::IO::IO::select([self],nil,nil,0.010)
          until selected
            pause
            selected = GxG::IO::IO::select([self],nil,nil,0.010)
          end
        rescue Exception
          pause
          retry
        end
        retry
      end
      #
      if result.is_a?(::Array)
        if result[0].is_a?(::String)
          result[0].force_encoding(::Encoding::ASCII_8BIT)
        end
      end
      if @after_receive_data.respond_to?(:call)
        @after_receive_data.call(result)
      end
    end
  else
    if self.closed?()
      raise IOError, "attempting to recvmsg on a closed stream"
    else
      if @before_receive_data.respond_to?(:call)
        @before_receive_data.call()
      end
      result = self.original_recvmsg(max_mesg_length, flags, max_control_length, options)
      if result.is_a?(::Array)
        if result[0].is_a?(::String)
          result[0].force_encoding(::Encoding::ASCII_8BIT)
        end
      end
      if @after_receive_data.respond_to?(:call)
        @after_receive_data.call(result)
      end
    end
  end
  result
end
send(data="", flags=0, destination_socket_address=nil, *controls) click to toggle source
# File lib/gxg/gxg_io.rb, line 181
def send(data="", flags=0, destination_socket_address=nil, *controls)
  # for now, flags ignored.  LATER: study Socket.send message flags in detail
  unless self.instance_variable_defined?(:@remote_closed)
    @remote_closed = nil
  end
  unless self.instance_variable_defined?(:@write_latency)
    @write_latency = {:low => nil, :last => nil, :high => nil}
  end
  unless self.instance_variable_defined?(:@before_send_data)
    @before_send_data = nil
  end
  unless self.instance_variable_defined?(:@after_send_data)
    @after_send_data = nil
  end
  if self.closed?()
    raise IOError, "attempting to send on a closed stream"
  else
    if @remote_closed
      raise Errno::EPIPE
    else
      data = data.to_s
      length = 0
      if data.bytesize() > 0
        length = data.bytesize()
        # process controls:
        if controls.size > 0
          if controls[0].is_a?(::Socket::AncillaryData)
            controls = controls[0]
          else
            if controls[0].is_a?(::Hash)
              controls = ::Socket::AncillaryData.new(::Socket::AF_UNIX, (controls[:level] || :SOCKET), (controls[:type] || ::Socket::SCM_RIGHTS), (controls[:data] || self.fileno))
            else
              controls.flatten!
              controls = ::Socket::AncillaryData.new(::Socket::AF_UNIX, (controls[0] || :SOCKET), (controls[1] || ::Socket::SCM_RIGHTS), (controls[2] || self.fileno))
            end
          end
        else
          controls = nil
        end
        #
        if self.is_any?(::UNIXServer, ::UNIXSocket, ::Socket)
          whatfor = :socket
        else
          if self.is_any?(::TCPServer, ::SOCKSocket, ::TCPSocket)
            whatfor = :tcp
          else
            if self.is_any?(::UDPServer, ::UDPSource, ::UDPSocket)
              whatfor = :udp
            end
          end
        end
        buffer_size = self.buffer_limits(:out,whatfor)[:initial]
        if length >= buffer_size
          passes = self.passes_needed(length, buffer_size)
        else
          passes = 1
        end
        #
        total = (length - 1)
        starting = 0
        ending = ([length,buffer_size].min - 1)
        #
        passes.times do
          # if remote end closes during write, raise error.
          if controls
            reading = self.latency_reading(self.write_latency()) do
              self.sendmsg(data.slice_bytes(starting..ending),flags,destination_socket_address,controls) or @remote_closed=true
            end
          else
            if destination_socket_address
              reading = self.latency_reading(self.write_latency()) do
                self.original_send(data.slice_bytes(starting..ending),flags,destination_socket_address) or @remote_closed=true
              end
            else
              reading = self.latency_reading(self.write_latency()) do
                self.original_send(data.slice_bytes(starting..ending),flags) or @remote_closed=true
              end
            end
          end
          if @remote_closed
            break
          else
            @write_latency = reading[:reading]
          end
          starting = ending + 1
          ending = (starting + (buffer_size - 1))
          if ending > total
            ending = ending - (ending - total)
          end
        end
        #
        if @remote_closed
          raise Errno::EPIPE
        end
      end
      length
    end
  end
end
sendmsg(data="", flags=0, destination_socket_address=nil, *controls) click to toggle source
# File lib/gxg/gxg_io.rb, line 8
def sendmsg(data="", flags=0, destination_socket_address=nil, *controls)
  unless self.instance_variable_defined?(:@remote_closed)
    @remote_closed = nil
  end
  unless self.instance_variable_defined?(:@write_latency)
    @write_latency = {:low => nil, :last => nil, :high => nil}
  end
  unless self.instance_variable_defined?(:@before_send_data)
    @before_send_data = nil
  end
  unless self.instance_variable_defined?(:@after_send_data)
    @after_send_data = nil
  end
  if self.fcntl(::Fcntl::F_GETFL,::Fcntl::O_NONBLOCK) & ::Fcntl::O_NONBLOCK == ::Fcntl::O_NONBLOCK
    # on Winderz, just to a buffer sized spoon full until done ??
    if self.closed?()
      raise IOError, "attempting to sendmsg on a closed stream"
    else
      if @remote_closed
        raise Errno::EPIPE
      else
        data = data.to_s
        written_bytes = data.bytesize()
        # process controls:
        if controls.size > 0
          if controls[0].is_a?(::Socket::AncillaryData)
            controls = controls[0]
          else
            if controls[0].is_a?(::Hash)
              controls = ::Socket::AncillaryData.new(::Socket::AF_UNIX, (controls[:level] || :SOCKET), (controls[:type] || ::Socket::SCM_RIGHTS), (controls[:data] || self.fileno))
            else
              controls.flatten!
              controls = ::Socket::AncillaryData.new(::Socket::AF_UNIX, (controls[0] || :SOCKET), (controls[1] || ::Socket::SCM_RIGHTS), (controls[2] || self.fileno))
            end
          end
        else
          raise ArgumentError, "you MUST supply ancillary data as a control"
        end
        #
        if self.is_any?(::UNIXServer, ::UNIXSocket, ::Socket)
          whatfor = :socket
        else
          if self.is_any?(::TCPServer, ::SOCKSocket, ::TCPSocket)
            whatfor = :tcp
          else
            if self.is_any?(::UDPServer, ::UDPSource, ::UDPSocket)
              whatfor = :udp
            end
          end
        end
        buffer_size = self.buffer_limits(:out,whatfor)[:initial]
        if written_bytes >= buffer_size
          passes = self.passes_needed(written_bytes, buffer_size)
        else
          passes = 1
        end
        #
        total = (written_bytes - 1)
        starting = 0
        ending = ([written_bytes,buffer_size].min - 1)
        #
        passes.times do
          #
          begin
            # if remote end closes during write, raise error by setting @remote_closed and breaking loop.
            reading = self.latency_reading(self.write_latency()) do
              self.sendmsg_nonblock(data.slice_bytes(starting..ending),flags,destination_socket_address,controls)
            end
            @write_latency = reading[:reading]
            #
          rescue ::IO::WaitWritable, GxG::IO::IO::WaitWritable, ::Errno::EINTR, ::Errno::EWOULDBLOCK, ::Errno::EAGAIN
            pause
            begin
              selected = ::IO::select(nil,[self],nil,0.010)
              until selected
                pause
                selected = ::IO::select(nil,[self],nil,0.010)
              end
            rescue Exception
              pause
              retry
            end
            retry
          rescue ::Errno::EPIPE
            @remote_closed=true
            break
          end
          #
          starting = ending + 1
          ending = (starting + (buffer_size - 1))
          if ending > total
            ending = ending - (ending - total)
          end
        end
        if @remote_closed
          raise Errno::EPIPE
        end
        written_bytes
      end
    end
  else
    if self.closed?()
      raise IOError, "attempting to sendmsg on a closed stream"
    else
      if @remote_closed
        raise Errno::EPIPE
      else
        data = self.transcode_to_external(data)
        written_bytes = data.bytesize()
        # process controls:
        if controls.size > 0
          if controls[0].is_a?(::Socket::AncillaryData)
            controls = controls[0]
          else
            if controls[0].is_a?(::Hash)
              controls = ::Socket::AncillaryData.new(::Socket::AF_UNIX, (controls[:level] || :SOCKET), (controls[:type] || ::Socket::SCM_RIGHTS), (controls[:data] || self.fileno))
            else
              controls.flatten!
              controls = ::Socket::AncillaryData.new(::Socket::AF_UNIX, (controls[0] || :SOCKET), (controls[1] || ::Socket::SCM_RIGHTS), (controls[2] || self.fileno))
            end
          end
        else
          raise ArgumentError, "you MUST supply ancillary data as a control"
        end
        #
        if self.is_any?(::UNIXServer, ::UNIXSocket, ::Socket)
          whatfor = :socket
        else
          if self.is_any?(::TCPServer, ::SOCKSocket, ::TCPSocket)
            whatfor = :tcp
          else
            if self.is_any?(::UDPServer, ::UDPSource, ::UDPSocket)
              whatfor = :udp
            end
          end
        end
        buffer_size = self.buffer_limits(:out,whatfor)[:initial]
        if written_bytes >= buffer_size
          passes = self.passes_needed(written_bytes, buffer_size)
        else
          passes = 1
        end
        #
        total = (written_bytes - 1)
        starting = 0
        ending = ([written_bytes,buffer_size].min - 1)
        #
        passes.times do
          # if remote end closes during write, raise error.
          reading = self.latency_reading(self.write_latency()) do
            self.original_sendmsg(data.slice_bytes(starting..ending),flags,destination_socket_address,controls) or @remote_closed=true
          end
          if @remote_closed
            break
          else
            @write_latency = reading[:reading]
          end
          starting = ending + 1
          ending = (starting + (buffer_size - 1))
          if ending > total
            ending = ending - (ending - total)
          end
        end
        if @remote_closed
          raise Errno::EPIPE
        end
        #
      end
      written_bytes
    end
  end
end