Class: ZMachine::TCPChannel

Inherits:
Channel
  • Object
show all
Defined in:
lib/zmachine/tcp_channel.rb

Instance Attribute Summary collapse

Attributes inherited from Channel

#handler, #selector, #socket

Instance Method Summary collapse

Constructor Details

#initialize(selector) ⇒ TCPChannel

Returns a new instance of TCPChannel.



10
11
12
13
14
# File 'lib/zmachine/tcp_channel.rb', line 10

def initialize(selector)
  super(selector)
  @close_scheduled = false
  @connect_pending = false
end

Instance Attribute Details

#connect_pendingObject

Returns the value of attribute connect_pending.



8
9
10
# File 'lib/zmachine/tcp_channel.rb', line 8

def connect_pending
  @connect_pending
end

Instance Method Details

#acceptObject



27
28
29
30
31
32
# File 'lib/zmachine/tcp_channel.rb', line 27

def accept
  client_socket = socket.accept
  return unless client_socket
  client_socket.configure_blocking(false)
  TCPChannel.new(client_socket, @selector)
end

#bind(address, port) ⇒ Object



20
21
22
23
24
25
# File 'lib/zmachine/tcp_channel.rb', line 20

def bind(address, port)
  address = InetSocketAddress.new(address, port)
  @socket = ServerSocketChannel.open
  @socket.configure_blocking(false)
  @socket.bind(address)
end

#closeObject



57
58
59
60
61
62
63
64
# File 'lib/zmachine/tcp_channel.rb', line 57

def close
  if @channel_key
    @channel_key.cancel
    @channel_key = nil
  end

  @socket.close rescue nil
end

#connect(address, port) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/zmachine/tcp_channel.rb', line 34

def connect(address, port)
  address = InetSocketAddress.new(address, port)
  @socket = SocketChannel.open
  @socket.configure_blocking(false)

  if socket.connect(address)
    # Connection returned immediately. Can happen with localhost
    # connections.
    # WARNING, this code is untested due to lack of available test
    # conditions.  Ought to be be able to come here from a localhost
    # connection, but that doesn't happen on Linux. (Maybe on FreeBSD?)
    # The reason for not handling this until we can test it is that we
    # really need to return from this function WITHOUT triggering any EM
    # events.  That's because until the user code has seen the signature
    # we generated here, it won't be able to properly dispatch them. The
    # C++ EM deals with this by setting pending mode as a flag in ALL
    # eventable descriptors and making the descriptor select for
    # writable. Then, it can send UNBOUND and CONNECTION_COMPLETED on the
    # next pass through the loop, because writable will fire.
    raise RuntimeError.new("immediate-connect unimplemented")
  end
end

#current_eventsObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/zmachine/tcp_channel.rb', line 152

def current_events
  if @socket.respond_to?(:accept)
    return SelectionKey::OP_ACCEPT
  end

  events = 0

  if @connect_pending
    events |= SelectionKey::OP_CONNECT
  else
    events |= SelectionKey::OP_READ
    events |= SelectionKey::OP_WRITE unless @outbound_queue.empty?
  end

  return events
end

#finish_connectingObject



105
106
107
108
109
110
# File 'lib/zmachine/tcp_channel.rb', line 105

def finish_connecting
  @socket.finish_connect
  @connect_pending = false
  update_events
  return true
end

#has_more?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/zmachine/tcp_channel.rb', line 148

def has_more?
  false
end

#peer_nameObject

TODO: fix these



125
126
127
128
# File 'lib/zmachine/tcp_channel.rb', line 125

def peer_name
  sock = @socket.socket
  [sock.port, sock.inet_address.host_address]
end

#read_inbound_data(buffer) ⇒ Object

Raises:

  • (IOException)


75
76
77
78
79
80
81
# File 'lib/zmachine/tcp_channel.rb', line 75

def read_inbound_data(buffer)
  buffer.clear
  raise IOException.new("eof") if @socket.read(buffer) == -1
  buffer.flip
  return if buffer.limit == 0
  String.from_java_bytes(buffer.array[buffer.position...buffer.limit])
end

#registerObject



16
17
18
# File 'lib/zmachine/tcp_channel.rb', line 16

def register
  @channel_key ||= @socket.register(@selector, current_events, self)
end

#schedule_close(after_writing) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/zmachine/tcp_channel.rb', line 112

def schedule_close(after_writing)
  @outbound_queue.clear unless after_writing

  if @outbound_queue.empty?
    return true
  else
    update_events
    @close_scheduled = true
    return false
  end
end

#send_data(data) ⇒ Object



66
67
68
69
70
71
72
73
# File 'lib/zmachine/tcp_channel.rb', line 66

def send_data(data)
  return if @close_scheduled
  buffer = ByteBuffer.wrap(data.to_java_bytes)
  if buffer.remaining() > 0
    @outbound_queue << buffer
    update_events
  end
end

#sock_nameObject



130
131
132
133
# File 'lib/zmachine/tcp_channel.rb', line 130

def sock_name
  sock = @socket.socket
  [sock.local_port, sock.local_address.host_address]
end

#update_eventsObject



140
141
142
143
144
145
146
# File 'lib/zmachine/tcp_channel.rb', line 140

def update_events
  return unless @channel_key
  events = current_events
  if @channel_key.interest_ops != events
    @channel_key.interest_ops(events)
  end
end

#write_outbound_dataObject



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/zmachine/tcp_channel.rb', line 83

def write_outbound_data
  until @outbound_queue.empty?
    buffer = @outbound_queue.first
    @socket.write(buffer) if buffer.remaining > 0

    # Did we consume the whole outbound buffer? If yes,
    # pop it off and keep looping. If no, the outbound network
    # buffers are full, so break out of here.
    if buffer.remaining == 0
      @outbound_queue.shift
    else
      break
    end
  end

  if @outbound_queue.empty? && !@close_scheduled
    update_events
  end

  return (@close_scheduled && @outbound_queue.empty?) ? false : true
end