lib/socket: Added more documentation on Socket class, also fixed a visibility issue...
[nit.git] / lib / socket / socket.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Socket services
18 module socket
19
20 import socket_c
21
22 # Portal for communication between two machines
23 class Socket
24 # IPv4 address the socket is connected to
25 # Formatted as xxx.xxx.xxx.xxx
26 var address: String
27
28 # Hostname of the socket connected to self
29 # In C : The real canonical host name (e.g. example.org)
30 var host: nullable String
31
32 # Port open for the socket
33 var port: Int
34
35 # Underlying C socket
36 private var socket: FFSocket
37
38 # Underlying C socket
39 private var addrin: FFSocketAddrIn
40
41 # Guard for errors
42 # If the socket could not be created or if the socket was destroyed
43 # before a call needing the socket was made
44 # this flag will be set to false.
45 var still_alive = true # Note : HUGE SUCCESS
46
47 # Creates a socket connection to host `thost` on port `port`
48 init stream_with_host(thost: String, tport: Int)
49 do
50 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
51 if socket.address_is_null then
52 still_alive = false
53 return
54 end
55 socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
56 var hostname = socket.gethostbyname(thost)
57 addrin = new FFSocketAddrIn.with_hostent(hostname, tport)
58 address = addrin.address
59 host = hostname.h_name
60 port = addrin.port
61 end
62
63 # Creates a server socket on port `tport`
64 init stream_with_port(tport: Int)
65 do
66 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
67 if socket.address_is_null then
68 still_alive = false
69 return
70 end
71 socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1)
72 addrin = new FFSocketAddrIn.with(tport, new FFSocketAddressFamilies.af_inet)
73 address = addrin.address
74 port = addrin.port
75 host = null
76 end
77
78 # Creates a client socket, this is meant to be used by accept only
79 private init primitive_init(h: FFSocketAcceptResult)
80 do
81 socket = h.socket
82 addrin = h.addrIn
83 address = addrin.address
84 port = addrin.port
85 host = null
86 end
87
88 # Returns an array containing an enum of the events ready to be read
89 #
90 # event_types : Combination of several event types to watch
91 #
92 # timeout : Time in milliseconds before stopping listening for events on this socket
93 #
94 private fun poll_in(event_types: Array[FFSocketPollValues], timeout: Int): Array[FFSocketPollValues] do
95 if not still_alive then return new Array[FFSocketPollValues]
96 return socket.socket_poll(new PollFD(socket.descriptor, event_types), timeout)
97 end
98
99 # Easier use of poll_in to check for something to read on all channels of any priority
100 #
101 # timeout : Time in milliseconds before stopping to wait for events
102 #
103 fun ready_to_read(timeout: Int): Bool
104 do
105 if not still_alive then return false
106 var events = new Array[FFSocketPollValues]
107 events.push(new FFSocketPollValues.pollin)
108 events.push(new FFSocketPollValues.pollrdnorm)
109 events.push(new FFSocketPollValues.pollpri)
110 events.push(new FFSocketPollValues.pollrdband)
111 return poll_in(events, timeout).length != 0
112 end
113
114 # Checks if the socket still is connected
115 #
116 fun connected: Bool
117 do
118 if not still_alive then return false
119 var events = new Array[FFSocketPollValues]
120 events.push(new FFSocketPollValues.pollhup)
121 events.push(new FFSocketPollValues.pollerr)
122 return poll_in(events, 0).length == 0
123 end
124
125 # Establishes a connection to socket addrin
126 #
127 # REQUIRES : self.still_alive
128 fun connect: Bool do
129 assert still_alive
130 return socket.connect(addrin) >= 0
131 end
132
133 # Write a message to connected socket
134 #
135 # Returns `true` if the `msg` was sent, `false` otherwise
136 #
137 # If not socket.sill_alive, false will be returned
138 fun write(msg: String): Bool do
139 if not still_alive then return false
140 return socket.write(msg) >= 0
141 end
142
143 # Read from connected socket
144 #
145 # If the socket is disconnected, an empty string will be returned
146 fun read: String do
147 if not still_alive then return ""
148 return socket.read
149 end
150
151 # Close connection
152 #
153 # Returns : `true` if the close was successful, `false` otherwise
154 fun close: Bool do
155 if not still_alive then return true
156 if socket.close >= 0 then
157 still_alive = false
158 return true
159 end
160 return false
161 end
162
163 # Associates the socket to a local address and port
164 #
165 # Returns : `true` if the socket could be bound, `false` otherwise
166 fun bind: Bool do
167 if not still_alive then return false
168 return socket.bind(addrin) >= 0
169 end
170
171 # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients
172 #
173 # Returns : `true` if the socket could be set, `false` otherwise
174 fun listen(size: Int): Bool do
175 if not still_alive then return false
176 return socket.listen(size) >= 0
177 end
178
179 # Accepts an incoming connection from a client
180 # This creates a new socket that represents the connection to a client
181 #
182 # Returns : the socket for communication with the client
183 #
184 # REQUIRES : self.still_alive
185 fun accept: Socket do
186 assert still_alive
187 return new Socket.primitive_init(socket.accept)
188 end
189
190 end
191
192 class SocketSet
193 var sset: FFSocketSet
194 init do sset = new FFSocketSet end
195 fun set(s: Socket) do sset.set(s.socket) end
196 fun is_set(s: Socket): Bool do return sset.is_set(s.socket) end
197 fun zero do sset.zero end
198 fun clear(s: Socket) do sset.clear(s.socket) end
199 end
200
201 class SocketObserver
202 private var observer: FFSocketObserver
203 var readset: nullable SocketSet = null
204 var writeset: nullable SocketSet = null
205 var exceptset: nullable SocketSet = null
206 init(read :Bool, write :Bool, except: Bool)
207 do
208 if read then readset = new SocketSet
209 if write then writeset = new SocketSet
210 if except then exceptset = new SocketSet
211 observer = new FFSocketObserver
212 end
213 fun select(max: Socket,seconds: Int, microseconds: Int): Bool
214 do
215 var timeval = new FFTimeval(seconds, microseconds)
216 return observer.select(max.socket, readset.sset, writeset.sset, readset.sset, timeval) > 0
217 end
218 end
219