1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
21 intrude import standard
::stream
23 # A general TCP socket, either a `TCPStream` or a `TCPServer`
27 private var socket
: NativeSocket is noinit
29 # Port used by the socket
32 # IPv4 address to which `self` is connected
34 # Formatted as xxx.xxx.xxx.xxx.
35 var address
: String is noinit
37 # Is this socket closed?
41 # Simple communication stream with a remote socket
48 # Real canonical name of the host to which `self` is connected
51 private var addrin
: NativeSocketAddrIn is noinit
53 redef var end_reached
= false
55 # TODO make init private
57 # Creates a socket connection to host `host` on port `port`
58 init connect
(host
: String, port
: Int)
60 _buffer
= new FlatBuffer
62 socket
= new NativeSocket.socket
(new NativeSocketAddressFamilies.af_inet
,
63 new NativeSocketTypes.sock_stream
, new NativeSocketProtocolFamilies.pf_null
)
64 if socket
.address_is_null
then
69 socket
.setsockopt
(new NativeSocketOptLevels.socket
, new NativeSocketOptNames.reuseaddr
, 1)
70 var hostname
= socket
.gethostbyname
(host
)
71 addrin
= new NativeSocketAddrIn.with_hostent
(hostname
, port
)
73 address
= addrin
.address
74 init(addrin
.port
, hostname
.h_name
)
76 closed
= not internal_connect
80 # Creates a client socket, this is meant to be used by accept only
81 private init server_side
(h
: SocketAcceptResult)
83 _buffer
= new FlatBuffer
87 address
= addrin
.address
89 init(addrin
.port
, address
)
92 redef fun poll_in
do return ready_to_read
(0)
94 # Returns an array containing an enum of the events ready to be read
96 # event_types : Combination of several event types to watch
98 # timeout : Time in milliseconds before stopping listening for events on this socket
99 private fun pollin
(event_types
: Array[NativeSocketPollValues], timeout
: Int): Array[NativeSocketPollValues] do
100 if end_reached
then return new Array[NativeSocketPollValues]
101 return socket
.socket_poll
(new PollFD(socket
.descriptor
, event_types
), timeout
)
104 # Easier use of pollin to check for something to read on all channels of any priority
106 # timeout : Time in milliseconds before stopping to wait for events
107 fun ready_to_read
(timeout
: Int): Bool
109 if _buffer_pos
< _buffer
.length
then return true
110 if eof
then return false
111 var events
= [new NativeSocketPollValues.pollin
]
112 return pollin
(events
, timeout
).length
!= 0
115 # Checks if the socket still is connected
118 if closed
then return false
119 var events
= [new NativeSocketPollValues.pollhup
, new NativeSocketPollValues.pollerr
]
120 if pollin
(events
, 0).length
== 0 then
128 redef fun is_writable
do return not end_reached
130 # Establishes a connection to socket addrin
132 # REQUIRES : not self.end_reached
133 private fun internal_connect
: Bool
136 return socket
.connect
(addrin
) >= 0
139 # If socket.end_reached, nothing will happen
140 redef fun write
(msg
: Text)
142 if closed
then return
143 socket
.write
(msg
.to_s
)
146 fun write_ln
(msg
: Text)
148 if end_reached
then return
153 redef fun fill_buffer
157 if not connected
then return
158 var read
= socket
.read
159 if read
.length
== 0 then
168 if closed
then return
169 if socket
.close
>= 0 then
174 # A socket listening on a given `port` for incomming connections
176 # Create streams to communicate with clients using `accept`.
180 private var addrin
: NativeSocketAddrIn is noinit
182 # Create and bind a listening server socket on port `port`
185 socket
= new NativeSocket.socket
(new NativeSocketAddressFamilies.af_inet
,
186 new NativeSocketTypes.sock_stream
, new NativeSocketProtocolFamilies.pf_null
)
187 assert not socket
.address_is_null
188 socket
.setsockopt
(new NativeSocketOptLevels.socket
, new NativeSocketOptNames.reuseaddr
, 1)
189 addrin
= new NativeSocketAddrIn.with
(port
, new NativeSocketAddressFamilies.af_inet
)
190 address
= addrin
.address
196 # Associates the socket to a local address and port
198 # Returns whether the socket has been be bound.
199 private fun bind
: Bool do
200 return socket
.bind
(addrin
) >= 0
203 # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients
205 # Returns `true` if the socket could be set, `false` otherwise
206 fun listen
(size
: Int): Bool do
207 return socket
.listen
(size
) >= 0
210 # Accepts an incoming connection from a client
212 # Create and return a new socket to the client. May return null if not
213 # `blocking` and there's no waiting clients, or upon an interruption
214 # (whether `blocking` or not).
216 # Require: not closed
217 fun accept
: nullable TCPStream
220 var native
= socket
.accept
221 if native
== null then return null
222 return new TCPStream.server_side
(native
)
229 # FIXME unify with `SocketStream::close` when we can use qualified names
231 if closed
then return
232 if socket
.close
>= 0 then
239 var sset
= new NativeSocketSet
240 fun set
(s
: TCPSocket) do sset
.set
(s
.socket
) end
241 fun is_set
(s
: TCPSocket): Bool do return sset
.is_set
(s
.socket
) end
242 fun zero
do sset
.zero
end
243 fun clear
(s
: TCPSocket) do sset
.clear
(s
.socket
) end
247 private var observer
: NativeSocketObserver
248 var readset
: nullable SocketSet = null
249 var writeset
: nullable SocketSet = null
250 var exceptset
: nullable SocketSet = null
251 init(read
:Bool, write
:Bool, except
: Bool)
253 if read
then readset
= new SocketSet
254 if write
then writeset
= new SocketSet
255 if except
then exceptset
= new SocketSet
256 observer
= new NativeSocketObserver
258 fun select
(max
: TCPSocket, seconds
: Int, microseconds
: Int): Bool
260 var timeval
= new NativeTimeval(seconds
, microseconds
)
261 return observer
.select
(max
.socket
, readset
.sset
, writeset
.sset
, readset
.sset
, timeval
) > 0