# 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