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)
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)
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
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