X-Git-Url: http://nitlanguage.org diff --git a/lib/socket/socket.nit b/lib/socket/socket.nit index 52094cc..8c96d17 100644 --- a/lib/socket/socket.nit +++ b/lib/socket/socket.nit @@ -19,24 +19,39 @@ module socket import socket_c +# Portal for communication between two machines class Socket + super BufferedIStream + super OStream + super PollableIStream + + # IPv4 address the socket is connected to + # Formatted as xxx.xxx.xxx.xxx var address: String + + # Hostname of the socket connected to self + # In C : The real canonical host name (e.g. example.org) var host: nullable String + + # Port open for the socket var port: Int + + # Underlying C socket private var socket: FFSocket + + # Underlying C socket private var addrin: FFSocketAddrIn - # Guard for errors - # If the socket could not be created or if the socket was destroyed - # before a call needing the socket was made - # this flag will be set to false. - var still_alive = true # Note : HUGE SUCCESS + redef var end_reached = false - init stream_with_host(thost: String, tport: Int) + # Creates a socket connection to host `thost` on port `port` + init client(thost: String, tport: Int) do + _buffer = new FlatBuffer + _buffer_pos = 0 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null ) if socket.address_is_null then - still_alive = false + end_reached = true return end socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1) @@ -45,13 +60,17 @@ class Socket address = addrin.address host = hostname.h_name port = addrin.port + if not end_reached then end_reached = not connect end - init stream_with_port(tport: Int) + # Creates a server socket on port `tport`, with a connection queue of size `max` + init server(tport: Int, max: Int) do + _buffer = new FlatBuffer + _buffer_pos = 0 socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null ) if socket.address_is_null then - still_alive = false + end_reached = true return end socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1) @@ -59,10 +78,15 @@ class Socket address = addrin.address port = addrin.port host = null + bind + listen(max) end - init primitive_init(h: FFSocketAcceptResult) + # Creates a client socket, this is meant to be used by accept only + private init primitive_init(h: FFSocketAcceptResult) do + _buffer = new FlatBuffer + _buffer_pos = 0 socket = h.socket addrin = h.addrIn address = addrin.address @@ -70,79 +94,117 @@ class Socket host = null end + redef fun poll_in do return ready_to_read(0) + # Returns an array containing an enum of the events ready to be read # # event_types : Combination of several event types to watch # # timeout : Time in milliseconds before stopping listening for events on this socket # - private fun poll_in(event_types: Array[FFSocketPollValues], timeout: Int): Array[FFSocketPollValues] do - if not still_alive then return new Array[FFSocketPollValues] + private fun pollin(event_types: Array[FFSocketPollValues], timeout: Int): Array[FFSocketPollValues] do + if end_reached then return new Array[FFSocketPollValues] return socket.socket_poll(new PollFD(socket.descriptor, event_types), timeout) end - # Easier use of poll_in to check for something to read on all channels of any priority + # Easier use of pollin to check for something to read on all channels of any priority # # timeout : Time in milliseconds before stopping to wait for events # fun ready_to_read(timeout: Int): Bool do - if not still_alive then return false + if _buffer_pos < _buffer.length then return true + if eof then return false var events = new Array[FFSocketPollValues] events.push(new FFSocketPollValues.pollin) - events.push(new FFSocketPollValues.pollrdnorm) - events.push(new FFSocketPollValues.pollpri) - events.push(new FFSocketPollValues.pollrdband) - return poll_in(events, timeout).length != 0 + return pollin(events, timeout).length != 0 end # Checks if the socket still is connected # fun connected: Bool do - if not still_alive then return false + if eof then return false var events = new Array[FFSocketPollValues] events.push(new FFSocketPollValues.pollhup) events.push(new FFSocketPollValues.pollerr) - return poll_in(events, 0).length == 0 + if pollin(events, 0).length == 0 then + return true + else + end_reached = true + return false + end end - fun connect: Bool do - assert still_alive + redef fun is_writable do return not end_reached + + # Establishes a connection to socket addrin + # + # REQUIRES : not self.end_reached + private fun connect: Bool + do + assert not end_reached return socket.connect(addrin) >= 0 end - fun write(msg: String): Bool do - if not still_alive then return false - return socket.write(msg) >= 0 + # If socket.end_reached, nothing will happen + redef fun write(msg: Text) + do + if end_reached then return + socket.write(msg.to_s) + end + + fun write_ln(msg: Text) + do + if end_reached then return + write(msg.to_s) + write("\n") end - fun read: String do - if not still_alive then return "" - return socket.read + redef fun fill_buffer + do + _buffer.clear + _buffer_pos = 0 + if not connected then return + var read = socket.read + if read.length == 0 then + close + end_reached = true + end + _buffer.append(read) end - fun close: Bool do - if not still_alive then return true + redef fun close do + if end_reached then return if socket.close >= 0 then - still_alive = false - return true + end_reached = true end - return false end - fun bind: Bool do - if not still_alive then return false + # Associates the socket to a local address and port + # + # Returns : `true` if the socket could be bound, `false` otherwise + private fun bind: Bool do + if end_reached then return false return socket.bind(addrin) >= 0 end - fun listen(size: Int): Bool do - if not still_alive then return false + # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients + # + # Returns : `true` if the socket could be set, `false` otherwise + private fun listen(size: Int): Bool do + if end_reached then return false return socket.listen(size) >= 0 end + # Accepts an incoming connection from a client + # This creates a new socket that represents the connection to a client + # + # Returns : the socket for communication with the client + # + # REQUIRES : not self.end_reached fun accept: Socket do - assert still_alive + assert not end_reached return new Socket.primitive_init(socket.accept) end