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 # Portal for communication between two machines
29 # IPv4 address the socket is connected to
30 # Formatted as xxx.xxx.xxx.xxx
31 var address
: String is noinit
33 # Hostname of the socket connected to self
34 # In C : The real canonical host name (e.g. example.org)
35 var host
: nullable String = null
37 # Port open for the socket
38 var port
: Int is noinit
41 private var socket
: FFSocket is noinit
44 private var addrin
: FFSocketAddrIn is noinit
46 redef var end_reached
= false
48 # Creates a socket connection to host `thost` on port `port`
49 init client
(thost
: String, tport
: Int)
51 _buffer
= new FlatBuffer
53 socket
= new FFSocket.socket
( new FFSocketAddressFamilies.af_inet
, new FFSocketTypes.sock_stream
, new FFSocketProtocolFamilies.pf_null
)
54 if socket
.address_is_null
then
58 socket
.setsockopt
(new FFSocketOptLevels.socket
, new FFSocketOptNames.reuseaddr
, 1)
59 var hostname
= socket
.gethostbyname
(thost
)
60 addrin
= new FFSocketAddrIn.with_hostent
(hostname
, tport
)
61 address
= addrin
.address
62 host
= hostname
.h_name
64 if not end_reached
then end_reached
= not connect
67 # Creates a server socket on port `tport`, with a connection queue of size `max`
68 init server
(tport
: Int, max
: Int)
70 _buffer
= new FlatBuffer
72 socket
= new FFSocket.socket
( new FFSocketAddressFamilies.af_inet
, new FFSocketTypes.sock_stream
, new FFSocketProtocolFamilies.pf_null
)
73 if socket
.address_is_null
then
77 socket
.setsockopt
(new FFSocketOptLevels.socket
, new FFSocketOptNames.reuseaddr
, 1)
78 addrin
= new FFSocketAddrIn.with
(tport
, new FFSocketAddressFamilies.af_inet
)
79 address
= addrin
.address
86 # Creates a client socket, this is meant to be used by accept only
87 private init primitive_init
(h
: FFSocketAcceptResult)
89 _buffer
= new FlatBuffer
93 address
= addrin
.address
98 redef fun poll_in
do return ready_to_read
(0)
100 # Returns an array containing an enum of the events ready to be read
102 # event_types : Combination of several event types to watch
104 # timeout : Time in milliseconds before stopping listening for events on this socket
106 private fun pollin
(event_types
: Array[FFSocketPollValues], timeout
: Int): Array[FFSocketPollValues] do
107 if end_reached
then return new Array[FFSocketPollValues]
108 return socket
.socket_poll
(new PollFD(socket
.descriptor
, event_types
), timeout
)
111 # Easier use of pollin to check for something to read on all channels of any priority
113 # timeout : Time in milliseconds before stopping to wait for events
115 fun ready_to_read
(timeout
: Int): Bool
117 if _buffer_pos
< _buffer
.length
then return true
118 if eof
then return false
119 var events
= new Array[FFSocketPollValues]
120 events
.push
(new FFSocketPollValues.pollin
)
121 return pollin
(events
, timeout
).length
!= 0
124 # Checks if the socket still is connected
128 if eof
then return false
129 var events
= new Array[FFSocketPollValues]
130 events
.push
(new FFSocketPollValues.pollhup
)
131 events
.push
(new FFSocketPollValues.pollerr
)
132 if pollin
(events
, 0).length
== 0 then
140 redef fun is_writable
do return not end_reached
142 # Establishes a connection to socket addrin
144 # REQUIRES : not self.end_reached
145 private fun connect
: Bool
147 assert not end_reached
148 return socket
.connect
(addrin
) >= 0
151 # If socket.end_reached, nothing will happen
152 redef fun write
(msg
: Text)
154 if end_reached
then return
155 socket
.write
(msg
.to_s
)
158 fun write_ln
(msg
: Text)
160 if end_reached
then return
165 redef fun fill_buffer
169 if not connected
then return
170 var read
= socket
.read
171 if read
.length
== 0 then
179 if end_reached
then return
180 if socket
.close
>= 0 then
185 # Associates the socket to a local address and port
187 # Returns : `true` if the socket could be bound, `false` otherwise
188 private fun bind
: Bool do
189 if end_reached
then return false
190 return socket
.bind
(addrin
) >= 0
193 # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients
195 # Returns : `true` if the socket could be set, `false` otherwise
196 private fun listen
(size
: Int): Bool do
197 if end_reached
then return false
198 return socket
.listen
(size
) >= 0
201 # Accepts an incoming connection from a client
202 # This creates a new socket that represents the connection to a client
204 # Returns : the socket for communication with the client
206 # REQUIRES : not self.end_reached
207 fun accept
: Socket do
208 assert not end_reached
209 return new Socket.primitive_init
(socket
.accept
)
215 var sset
= new FFSocketSet
216 fun set
(s
: Socket) do sset
.set
(s
.socket
) end
217 fun is_set
(s
: Socket): Bool do return sset
.is_set
(s
.socket
) end
218 fun zero
do sset
.zero
end
219 fun clear
(s
: Socket) do sset
.clear
(s
.socket
) end
223 private var observer
: FFSocketObserver
224 var readset
: nullable SocketSet = null
225 var writeset
: nullable SocketSet = null
226 var exceptset
: nullable SocketSet = null
227 init(read
:Bool, write
:Bool, except
: Bool)
229 if read
then readset
= new SocketSet
230 if write
then writeset
= new SocketSet
231 if except
then exceptset
= new SocketSet
232 observer
= new FFSocketObserver
234 fun select
(max
: Socket,seconds
: Int, microseconds
: Int): Bool
236 var timeval
= new FFTimeval(seconds
, microseconds
)
237 return observer
.select
(max
.socket
, readset
.sset
, writeset
.sset
, readset
.sset
, timeval
) > 0