Class: Socketry::TCP::Socket
- Inherits:
-
Object
- Object
- Socketry::TCP::Socket
- Includes:
- Socketry::Timeout
- Defined in:
- lib/socketry/tcp/socket.rb
Overview
Transmission Control Protocol sockets: Provide stream-like semantics
Direct Known Subclasses
Constant Summary
Constants included from Socketry::Timeout
Socketry::Timeout::DEFAULT_TIMEOUTS, Socketry::Timeout::DEFAULT_TIMER
Instance Attribute Summary collapse
-
#local_addr ⇒ Object
readonly
Returns the value of attribute local_addr.
-
#local_port ⇒ Object
readonly
Returns the value of attribute local_port.
-
#read_timeout ⇒ Object
readonly
Returns the value of attribute read_timeout.
-
#remote_addr ⇒ Object
readonly
Returns the value of attribute remote_addr.
-
#remote_port ⇒ Object
readonly
Returns the value of attribute remote_port.
-
#resolver ⇒ Object
readonly
Returns the value of attribute resolver.
-
#socket_class ⇒ Object
readonly
Returns the value of attribute socket_class.
-
#write_timeout ⇒ Object
readonly
Returns the value of attribute write_timeout.
Class Method Summary collapse
-
.connect(remote_addr, remote_port, **args) ⇒ Socketry::TCP::Socket
Create a Socketry::TCP::Socket with the default options, then connect to the given host.
Instance Method Summary collapse
-
#close ⇒ true, false
Close the socket.
-
#connect(remote_addr, remote_port, local_addr: nil, local_port: nil, timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:connect]) ⇒ self
Connect to a remote host.
-
#connected? ⇒ true, false
Is the socket currently connected?.
-
#from_socket(socket) ⇒ Object
Wrap a Ruby/low-level socket in an Socketry::TCP::Socket.
-
#initialize(read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read], write_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:write], timer: Socketry::Timeout::DEFAULT_TIMER.new, resolver: Socketry::Resolver::DEFAULT_RESOLVER, socket_class: ::Socket) ⇒ Socketry::TCP::Socket
constructor
Create an unconnected Socketry::TCP::Socket.
-
#nodelay ⇒ true, false
Check whether Nagle’s algorithm has been disabled.
-
#nodelay=(flag) ⇒ Object
Disable or enable Nagle’s algorithm.
-
#read_nonblock(size, outbuf: nil) ⇒ String, :wait_readable
Perform a non-blocking read operation.
-
#readpartial(size, outbuf: nil, timeout: @read_timeout) ⇒ String
Read a partial amounth of data, blocking until it becomes available.
-
#reconnect(timeout: ) ⇒ Object
Re-establish a lost TCP connection.
-
#to_io ⇒ IO
Return a raw Ruby I/O object.
-
#write_nonblock(data) ⇒ Fixnum, :wait_writable
Perform a non-blocking write operation.
-
#writepartial(data, timeout: @write_timeout) ⇒ Fixnum, :wait_writable
Write a partial amounth of data, blocking until it’s completed.
Methods included from Socketry::Timeout
#clear_timeout, #lifetime, #set_timeout, #start_timer, #time_remaining
Constructor Details
#initialize(read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read], write_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:write], timer: Socketry::Timeout::DEFAULT_TIMER.new, resolver: Socketry::Resolver::DEFAULT_RESOLVER, socket_class: ::Socket) ⇒ Socketry::TCP::Socket
Create an unconnected Socketry::TCP::Socket
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/socketry/tcp/socket.rb', line 31 def initialize( read_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:read], write_timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:write], timer: Socketry::Timeout::DEFAULT_TIMER.new, resolver: Socketry::Resolver::DEFAULT_RESOLVER, socket_class: ::Socket ) @read_timeout = read_timeout @write_timeout = write_timeout @socket_class = socket_class @resolver = resolver @family = nil @socket = nil @remote_addr = nil @remote_port = nil @local_addr = nil @local_port = nil start_timer(timer) end |
Instance Attribute Details
#local_addr ⇒ Object (readonly)
Returns the value of attribute local_addr.
10 11 12 |
# File 'lib/socketry/tcp/socket.rb', line 10 def local_addr @local_addr end |
#local_port ⇒ Object (readonly)
Returns the value of attribute local_port.
10 11 12 |
# File 'lib/socketry/tcp/socket.rb', line 10 def local_port @local_port end |
#read_timeout ⇒ Object (readonly)
Returns the value of attribute read_timeout.
11 12 13 |
# File 'lib/socketry/tcp/socket.rb', line 11 def read_timeout @read_timeout end |
#remote_addr ⇒ Object (readonly)
Returns the value of attribute remote_addr.
10 11 12 |
# File 'lib/socketry/tcp/socket.rb', line 10 def remote_addr @remote_addr end |
#remote_port ⇒ Object (readonly)
Returns the value of attribute remote_port.
10 11 12 |
# File 'lib/socketry/tcp/socket.rb', line 10 def remote_port @remote_port end |
#resolver ⇒ Object (readonly)
Returns the value of attribute resolver.
11 12 13 |
# File 'lib/socketry/tcp/socket.rb', line 11 def resolver @resolver end |
#socket_class ⇒ Object (readonly)
Returns the value of attribute socket_class.
11 12 13 |
# File 'lib/socketry/tcp/socket.rb', line 11 def socket_class @socket_class end |
#write_timeout ⇒ Object (readonly)
Returns the value of attribute write_timeout.
11 12 13 |
# File 'lib/socketry/tcp/socket.rb', line 11 def write_timeout @write_timeout end |
Class Method Details
.connect(remote_addr, remote_port, **args) ⇒ Socketry::TCP::Socket
Create a Socketry::TCP::Socket with the default options, then connect to the given host.
19 20 21 |
# File 'lib/socketry/tcp/socket.rb', line 19 def self.connect(remote_addr, remote_port, **args) new.connect(remote_addr, remote_port, **args) end |
Instance Method Details
#close ⇒ true, false
Close the socket
245 246 247 248 249 250 251 |
# File 'lib/socketry/tcp/socket.rb', line 245 def close return false unless connected? @socket.close true ensure @socket = nil end |
#connect(remote_addr, remote_port, local_addr: nil, local_port: nil, timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:connect]) ⇒ self
Connect to a remote host
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/socketry/tcp/socket.rb', line 66 def connect( remote_addr, remote_port, local_addr: nil, local_port: nil, timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:connect] ) ensure_disconnected @remote_addr = remote_addr @remote_port = remote_port @local_addr = local_addr @local_port = local_port begin set_timeout(timeout) remote_addr = @resolver.resolve(remote_addr, timeout: time_remaining(timeout)) local_addr = @resolver.resolve(local_addr, timeout: time_remaining(timeout)) if local_addr raise ArgumentError, "expected IPAddr from resolver, got #{remote_addr.class}" unless remote_addr.is_a?(IPAddr) if remote_addr.ipv4? @family = ::Socket::AF_INET elsif remote_addr.ipv6? @family = ::Socket::AF_INET6 else raise Socketry::AddressError, "unsupported IP address family: #{remote_addr}" end socket = @socket_class.new(@family, ::Socket::SOCK_STREAM, 0) socket.bind Addrinfo.tcp(local_addr.to_s, local_port) if local_addr remote_sockaddr = ::Socket.sockaddr_in(remote_port, remote_addr.to_s) # Note: `exception: false` for Socket#connect_nonblock is only supported in Ruby 2.3+ begin socket.connect_nonblock(remote_sockaddr) rescue Errno::EINPROGRESS, Errno::EALREADY # Earlier JRuby 9.x versions do not seem to correctly support Socket#wait_writable in this case # Newer versions seem to behave correctly retry if IO.select(nil, [socket], nil, time_remaining(timeout)) socket.close raise Socketry::TimeoutError, "connection to #{remote_addr}:#{remote_port} timed out" rescue Errno::EISCONN # Sometimes raised when we've connected successfully end @socket = socket ensure clear_timeout(timeout) end self end |
#connected? ⇒ true, false
Is the socket currently connected?
This method returns the local connection state. However, it’s possible the remote side has closed the connection, so it’s not actually possible to actually know if the socket is actually still open without reading from or writing to it. It’s sort of like the Heisenberg uncertainty principle of sockets.
262 263 264 |
# File 'lib/socketry/tcp/socket.rb', line 262 def connected? @socket != nil end |
#from_socket(socket) ⇒ Object
Wrap a Ruby/low-level socket in an Socketry::TCP::Socket
133 134 135 136 137 138 |
# File 'lib/socketry/tcp/socket.rb', line 133 def from_socket(socket) ensure_disconnected raise TypeError, "expected #{@socket_class}, got #{socket.class}" unless socket.is_a?(@socket_class) @socket = socket self end |
#nodelay ⇒ true, false
Check whether Nagle’s algorithm has been disabled
221 222 223 224 |
# File 'lib/socketry/tcp/socket.rb', line 221 def nodelay ensure_connected @socket.getsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY).int.nonzero? end |
#nodelay=(flag) ⇒ Object
Disable or enable Nagle’s algorithm
229 230 231 232 |
# File 'lib/socketry/tcp/socket.rb', line 229 def nodelay=(flag) ensure_connected @socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, flag ? 1 : 0) end |
#read_nonblock(size, outbuf: nil) ⇒ String, :wait_readable
Perform a non-blocking read operation
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/socketry/tcp/socket.rb', line 146 def read_nonblock(size, outbuf: nil) ensure_connected case outbuf when String @socket.read_nonblock(size, outbuf, exception: false) when NilClass @socket.read_nonblock(size, exception: false) else raise TypeError, "unexpected outbuf class: #{outbuf.class}" end rescue IO::WaitReadable # Some buggy Rubies continue to raise this exception :wait_readable rescue IOError => ex raise Socketry::Error, ex., ex.backtrace end |
#readpartial(size, outbuf: nil, timeout: @read_timeout) ⇒ String
Read a partial amounth of data, blocking until it becomes available
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/socketry/tcp/socket.rb', line 167 def readpartial(size, outbuf: nil, timeout: @read_timeout) set_timeout(timeout) begin while (result = read_nonblock(size, outbuf: outbuf)) == :wait_readable next if @socket.wait_readable(read_timeout) raise TimeoutError, "read timed out after #{timeout} seconds" end ensure clear_timeout(timeout) end result || :eof end |
#reconnect(timeout: ) ⇒ Object
Re-establish a lost TCP connection
124 125 126 127 128 |
# File 'lib/socketry/tcp/socket.rb', line 124 def reconnect(timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:connect]) ensure_disconnected raise StateError, "can't reconnect: never completed initial connection" unless @remote_addr connect(@remote_addr, @remote_port, local_addr: @local_addr, local_port: @local_port, timeout: timeout) end |
#to_io ⇒ IO
Return a raw Ruby I/O object
237 238 239 240 |
# File 'lib/socketry/tcp/socket.rb', line 237 def to_io ensure_connected ::IO.try_convert(@socket) end |
#write_nonblock(data) ⇒ Fixnum, :wait_writable
Perform a non-blocking write operation
187 188 189 190 191 192 193 194 195 |
# File 'lib/socketry/tcp/socket.rb', line 187 def write_nonblock(data) ensure_connected @socket.write_nonblock(data, exception: false) rescue IO::WaitWriteable # Some buggy Rubies continue to raise this exception :wait_writable rescue IOError => ex raise Socketry::Error, ex., ex.backtrace end |
#writepartial(data, timeout: @write_timeout) ⇒ Fixnum, :wait_writable
Write a partial amounth of data, blocking until it’s completed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/socketry/tcp/socket.rb', line 202 def writepartial(data, timeout: @write_timeout) set_timeout(timeout) begin while (result = write_nonblock(data)) == :wait_writable next if @socket.wait_writable(read_timeout) raise TimeoutError, "write timed out after #{timeout} seconds" end ensure clear_timeout(timeout) end result || :eof end |