# Socket services
module socket
-import socket_c
+private import socket_c
intrude import standard::stream
# A general TCP socket, either a `TCPStream` or a `TCPServer`
# Simple communication stream with a remote socket
class TCPStream
super Socket
- super BufferedIStream
- super OStream
- super PollableIStream
+ super BufferedReader
+ super Writer
+ super PollableReader
# Real canonical name of the host to which `self` is connected
var host: String
# Creates a socket connection to host `host` on port `port`
init connect(host: String, port: Int)
do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet,
new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null)
closed = true
return
end
- socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1)
+ if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then
+ end_reached = true
+ closed = true
+ return
+ end
var hostname = socket.gethostbyname(host)
addrin = new NativeSocketAddrIn.with_hostent(hostname, port)
# Creates a client socket, this is meant to be used by accept only
private init server_side(h: SocketAcceptResult)
do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
socket = h.socket
addrin = h.addr_in
# timeout : Time in milliseconds before stopping to wait for events
fun ready_to_read(timeout: Int): Bool
do
- if _buffer_pos < _buffer.length then return true
- if eof then return false
+ if _buffer_pos < _buffer_length then return true
+ if end_reached then return false
var events = [new NativeSocketPollValues.pollin]
return pollin(events, timeout).length != 0
end
socket.write(msg.to_s)
end
+ redef fun write_byte(value)
+ do
+ if closed then return
+ socket.write_byte value
+ end
+
+ redef fun write_bytes(s) do
+ if closed then return
+ socket.write(s.to_s)
+ end
+
fun write_ln(msg: Text)
do
if end_reached then return
redef fun fill_buffer
do
- _buffer.clear
+ _buffer_length = 0
_buffer_pos = 0
if not connected then return
var read = socket.read
close
end_reached = true
end
- _buffer.append(read)
+ enlarge(_buffer_capacity + read.length)
+ read.copy_to_native(_buffer, read.length, 0, 0)
+ _buffer_length = read.length
+ end
+
+ fun enlarge(len: Int) do
+ if _buffer_capacity >= len then return
+ while _buffer_capacity < len do _buffer_capacity *= 2
+ var ns = new NativeString(_buffer_capacity)
+ _buffer.copy_to(ns, _buffer_length - _buffer_pos, _buffer_pos, 0)
+ _buffer = ns
end
redef fun close
if closed then return
if socket.close >= 0 then
closed = true
+ end_reached = true
+ end
+ end
+
+ # Send the data present in the socket buffer
+ fun flush
+ do
+ if not socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 1) or
+ not socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 0) then
+ closed = true
end
end
+end
# A socket listening on a given `port` for incomming connections
#
socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet,
new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null)
assert not socket.address_is_null
- socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1)
- addrin = new NativeSocketAddrIn.with(port, new NativeSocketAddressFamilies.af_inet)
+ if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then
+ closed = true
+ return
+ end
+ addrin = new NativeSocketAddrIn.with_port(port, new NativeSocketAddressFamilies.af_inet)
address = addrin.address
# Bind it
return new TCPStream.server_side(native)
end
+ # Set whether calls to `accept` are blocking
+ fun blocking=(value: Bool)
+ do
+ # We use the opposite from the native version as the native API
+ # is closer to the C API. In the Nity API, we use a positive version
+ # of the name.
+ socket.non_blocking = not value
+ end
# Close this socket
fun close
end
end
+# A simple set of sockets used by `SocketObserver`
class SocketSet
- var sset = new NativeSocketSet
- fun set(s: TCPSocket) do sset.set(s.socket) end
- fun is_set(s: TCPSocket): Bool do return sset.is_set(s.socket) end
- fun zero do sset.zero end
- fun clear(s: TCPSocket) do sset.clear(s.socket) end
+ private var native = new NativeSocketSet
+
+ init do clear
+
+ # Add `socket` to this set
+ fun add(socket: Socket) do native.set(socket.socket)
+
+ # Remove `socket` from this set
+ fun remove(socket: Socket) do native.clear(socket.socket)
+
+ # Does this set has `socket`?
+ fun has(socket: Socket): Bool do return native.is_set(socket.socket)
+
+ # Clear all sockets from this set
+ fun clear do native.zero
end
+# Service class to manage calls to `select`
class SocketObserver
- private var observer: NativeSocketObserver
- var readset: nullable SocketSet = null
- var writeset: nullable SocketSet = null
- var exceptset: nullable SocketSet = null
- init(read :Bool, write :Bool, except: Bool)
- do
- if read then readset = new SocketSet
- if write then writeset = new SocketSet
- if except then exceptset = new SocketSet
- observer = new NativeSocketObserver
+ private var native = new NativeSocketObserver
+
+ var read_set: nullable SocketSet = null
+
+ var write_set: nullable SocketSet = null
+
+ var except_set: nullable SocketSet = null
+
+ init(read: Bool, write: Bool, except: Bool)
+ is old_style_init do
+ if read then read_set = new SocketSet
+ if write then write_set = new SocketSet
+ if except then except_set = new SocketSet
end
- fun select(max: TCPSocket, seconds: Int, microseconds: Int): Bool
+
+ fun select(max: Socket, seconds: Int, microseconds: Int): Bool
do
+ # FIXME this implementation (see the call to nullable attributes below) and
+ # `NativeSockectObserver::select` is not stable.
+
var timeval = new NativeTimeval(seconds, microseconds)
- return observer.select(max.socket, readset.sset, writeset.sset, readset.sset, timeval) > 0
+ return native.select(max.socket, read_set.native, write_set.native, except_set.native, timeval) > 0
end
end
-