X-Git-Url: http://nitlanguage.org diff --git a/lib/socket/socket.nit b/lib/socket/socket.nit index f0aaa85..a40cc5d 100644 --- a/lib/socket/socket.nit +++ b/lib/socket/socket.nit @@ -17,7 +17,7 @@ # 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` @@ -41,9 +41,9 @@ end # 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 @@ -57,7 +57,7 @@ class TCPStream # 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) @@ -66,21 +66,40 @@ class TCPStream closed = true return end - socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) - var hostname = socket.gethostbyname(host) - addrin = new NativeSocketAddrIn.with_hostent(hostname, port) + if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then + end_reached = true + closed = true + return + end + + var hostname = sys.gethostbyname(host.to_cstring) + if hostname.address_is_null then + # Error in name lookup + var err = sys.h_errno + last_error = new IOError(err.to_s) + + closed = true + end_reached = true + + return + end + addrin = new NativeSocketAddrIn.with_hostent(hostname, port) address = addrin.address init(addrin.port, hostname.h_name) closed = not internal_connect end_reached = closed + if closed then + # Connection failed + last_error = new IOError(errno.strerror) + end end # 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 @@ -106,8 +125,8 @@ class TCPStream # 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 @@ -137,12 +156,23 @@ class TCPStream end # If socket.end_reached, nothing will happen - redef fun write(msg: Text) + redef fun write(msg) do if closed then return 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 @@ -152,7 +182,7 @@ class TCPStream redef fun fill_buffer do - _buffer.clear + _buffer_length = 0 _buffer_pos = 0 if not connected then return var read = socket.read @@ -160,7 +190,17 @@ class TCPStream close end_reached = true end - _buffer.append(read) + enlarge(_buffer_length + 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 = _buffer_capacity * 2 + 2 + var ns = new NativeString(_buffer_capacity) + _buffer.copy_to(ns, _buffer_length - _buffer_pos, _buffer_pos, 0) + _buffer = ns end redef fun close @@ -168,8 +208,19 @@ class TCPStream 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 # @@ -185,8 +236,11 @@ class TCPServer 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 @@ -222,6 +276,14 @@ class TCPServer 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 @@ -235,30 +297,48 @@ class TCPServer 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 -