# # $Id: netclient.icn,v 1.5 2004/10/10 17:45:20 rparlett Exp $ # package net import util link posix $include "posix.icn" # # Common base class for a client class which holds a connection to a # server and communicates using CRLF-terminated lines. # class NetClient:Connectable:Error:SetFields(server, port, connection, sbuff, timeout) # # Fail and set the error code, and close the connection. # @p method error_and_close(a) error(a) close() end # # Set the timeout in ms to use. If a figure of zero, or &null, is given (the default) # then no timeout will be used. # method set_timeout(timeout) return self.timeout := timeout end # # Open the connection. # method open() local s, t close() | fail s := server || ":" || port fire("Connecting", "Connecting to " || s) if not(connection := ::open(s, "n", timeout)) then { if &errno = 0 then return error("Timed out connecting to " || s) else return error("Couldn't connect to " || s) } sbuff := "" fire("Connected") return connection end # # Close the connection # method close() if \connection then { ::close(connection) | return error("Failed to close connection") fire("Closed") connection := &null } return end # # Set the server to use # method set_server(s) server := s end # # Set the port. # method set_port(n) port := n end # # @p method send(s) local t if /timeout | (timeout = 0) then return writes(connection, s) fcntl(connection, "F", "d") t := curr_time_millis() repeat { if writes(connection,s) then return if curr_time_millis() - t > timeout then return error("Send timed out") sleep(50) } end # # @p method recv(len) if /timeout | (timeout = 0) | (*select(connection, timeout) > 0) then return sysread(connection, len) | error("EOF encountered") else return error("Failed to select") end # # Write the given string to the connection. # method write_str(s) fire("Sending string", s) return send(s) end # # Write a single line to the connection, appending CRLF to the output stream. # @p method write_line(s) /s := "" fire("Sending line", s) return send(s || "\r\n") end # # Read a single line from the connection, and return it. # @p method read_line() local line repeat { # # Look for a line and if found return it. # sbuff ? { if line := tab(find("\r\n")) then { move(2) sbuff := tab(0) fire("Read line", line) return line } } sbuff ||:= recv(1024) | fail } end # # Read up to len bytes on the connection. # method read_str(len) local t # # Use the line buffer if it has anything in it. # if *sbuff > 0 then { sbuff ? { t := move(len) | tab(0) sbuff := tab(0) } } else { t := recv(len) | fail } fire("Read string", t) return t end method set_one(attr, val) case attr of { "timeout": set_timeout(int_val(attr, val)) "server": set_server(string_val(attr, val)) "port": set_port(int_val(attr, val)) default : field_error("Unknown attribute " || attr) } end initially() self.Connectable.initially() sbuff := "" end