(********************************************************************) (* *) (* socket.s7i File implementation type for sockets *) (* Copyright (C) 2007 - 2009, 2011 Thomas Mertes *) (* *) (* This file is part of the Seed7 Runtime Library. *) (* *) (* The Seed7 Runtime Library is free software; you can *) (* redistribute it and/or modify it under the terms of the GNU *) (* Lesser General Public License as published by the Free Software *) (* Foundation; either version 2.1 of the License, or (at your *) (* option) any later version. *) (* *) (* The Seed7 Runtime Library is distributed in the hope that it *) (* will be useful, but WITHOUT ANY WARRANTY; without even the *) (* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *) (* PURPOSE. See the GNU Lesser General Public License for more *) (* details. *) (* *) (* You should have received a copy of the GNU Lesser General *) (* Public License along with this program; if not, write to the *) (* Free Software Foundation, Inc., 51 Franklin Street, *) (* Fifth Floor, Boston, MA 02110-1301, USA. *) (* *) (********************************************************************) include "null_file.s7i"; include "enable_io.s7i"; include "duration.s7i"; include "sockbase.s7i"; const integer: SOCK_STREAM is 1; const integer: SOCK_DGRAM is 2; (** * Interface type for listeners. * The listener interface is implemented with inetListener. * A listener manages its accepted sockets. *) const type: listener is sub object interface; const proc: close (inout listener: aListener) is DYNAMIC; const proc: listen (in listener: aListener, in integer: backlog) is DYNAMIC; const func file: accept (inout listener: aListener) is DYNAMIC; const proc: signOn (inout listener: aListener, in file: sock) is DYNAMIC; const proc: signOff (inout listener: aListener, in file: sock) is DYNAMIC; const proc: waitForRequest (inout listener: aListener) is DYNAMIC; const func file: getExistingConnection (in listener: aListener) is DYNAMIC; const func file: getNewConnection (in listener: aListener) is DYNAMIC; const type: baseListener is new struct end struct; type_implements_interface(baseListener, listener); const listener: (attr listener) . value is baseListener.value; (* Operations for socket files *) (** * [[file|File]] implementation type for OS sockets. * This type supports communication via sockets. A ''socket'' is * not seekable. The functions [[null_file#length(in_null_file)|length]], * [[null_file#seek(in_null_file,in_integer)|seek]] and * [[null_file#tell(in_null_file)|tell]] raise FILE_ERROR. *) const type: socket is sub null_file struct var PRIMITIVE_SOCKET: sock is PRIMITIVE_NULL_SOCKET; var socketAddress: addr is socketAddress.value; var string: service is ""; var listener: acceptedFrom is listener.value; end struct; type_implements_interface(socket, file); const func socketAddress: localAddress (in file: aFile) is DYNAMIC; const func socketAddress: peerAddress (in file: aFile) is DYNAMIC; (** * Get the local address of the socket 'aSocket'. * @return the address to which the socket 'aSocket' is bound. * @exception FILE_ERROR A system function returns an error. * @exception MEMORY_ERROR Not enough memory to represent the result. *) const func socketAddress: localAddress (in socket: aSocket) is return localAddress(aSocket.sock); (** * Get the address of the peer to which 'aSocket' is connected. * @return the address of the peer connected to the socket 'aSocket'. * @exception FILE_ERROR A system function returns an error. * @exception MEMORY_ERROR Not enough memory to represent the result. *) const func socketAddress: peerAddress (in socket: aSocket) is return peerAddress(aSocket.sock); const func string: service (in file: aFile) is DYNAMIC; const func string: service (in socket: aSocket) is return aSocket.service; const func integer: port (in file: aFile) is DYNAMIC; const func integer: port (in socket: aSocket) is return integer(service(aSocket.addr)); const func integer: ord (in file: aFile) is DYNAMIC; const func integer: ord (in socket: aSocket) is return ord(aSocket.sock); const func boolean: inputReady (in file: inFile, in duration: timeout) is DYNAMIC; const func boolean: inputReady (in socket: inSocket, in duration: timeout) is return inputReady(inSocket.sock, toSeconds(timeout), timeout.micro_second); (** * Return a connected socket file for the given [[sockbase|socket address]]. * @return the socket file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception FILE_ERROR A system function returns an error. * @exception MEMORY_ERROR An out of memory situation occurred. *) const func file: openSocket (in socketAddress: address) is func result var file: newSocket is STD_NULL; local var PRIMITIVE_SOCKET: open_socket is PRIMITIVE_NULL_SOCKET; var socket: new_socket is socket.value; begin if address <> socketAddress.value then open_socket := PRIMITIVE_SOCKET(addrFamily(address), SOCK_STREAM, 0); if open_socket <> PRIMITIVE_NULL_SOCKET then block connect(open_socket, address); new_socket.addr := address; new_socket.service := service(address); new_socket.sock := open_socket; newSocket := toInterface(new_socket); exception catch FILE_ERROR: close(open_socket); end block; end if; end if; end func; (** * Return a connected internet socket file at a port at localhost. * @return the socket file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception FILE_ERROR A system function returns an error. * @exception RANGE_ERROR The port is not in the range 0 to 65535. * @exception MEMORY_ERROR An out of memory situation occurred. *) const func file: openInetSocket (in integer: portNumber) is return openSocket(inetSocketAddress(portNumber)); (** * Return a connected internet socket file at a port at ''hostName''. * Here ''hostName'' is either a host name (e.g.: "www.example.org"), * or an IPv4 address in standard dot notation (e.g.: "192.0.2.235"). * Operating systems supporting IPv6 may also accept an IPv6 address * in colon notation. * @return the socket file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened. * @exception FILE_ERROR A system function returns an error. * @exception RANGE_ERROR The port is not in the range 0 to 65535. * @exception MEMORY_ERROR An out of memory situation occurred. *) const func file: openInetSocket (in string: hostName, in integer: portNumber) is return openSocket(inetSocketAddress(hostName, portNumber)); (** * Close the socket ''aSocket''. * A listener manages accepted sockets (its existing connections). * When closing a socket, that was accepted from a listener, * it is also signed off from the listener. * @exception FILE_ERROR A system function returns an error. *) const proc: close (inout socket: aSocket) is func begin if aSocket.acceptedFrom <> listener.value then signOff(aSocket.acceptedFrom, aSocket); end if; close(aSocket.sock); aSocket.sock := PRIMITIVE_NULL_SOCKET; end func; const proc: release (inout file: aFile) is DYNAMIC; const proc: release (inout socket: aSocket) is func begin aSocket.acceptedFrom := listener.value; end func; (** * Forces that all buffered data of ''outSocket'' is sent to its destination. * Flushing a socket has no effect. *) const proc: flush (in socket: outSocket) is func begin noop; # flush(outSocket.sock); end func; (** * Write the [[string]] ''stri'' to ''outSocket''. * @exception FILE_ERROR The system function is not able to write * all characters of the string. * @exception RANGE_ERROR The string contains a character that does * not fit into a byte. *) const proc: write (in socket: outSocket, in string: stri) is func begin write(outSocket.sock, stri); end func; (** * Write a [[string]] followed by end-of-line to ''outSocket''. * This function assures that string and '\n' are sent together. * @exception FILE_ERROR The system function is not able to write * all characters of the string. * @exception RANGE_ERROR The string contains a character that does * not fit into a byte. *) const proc: writeln (in socket: outSocket, in string: stri) is func begin write(outSocket.sock, stri & "\n"); end func; (** * Read a character from ''inSocket''. * @return the character read. *) const func char: getc (inout socket: inSocket) is return getc(inSocket.sock, inSocket.bufferChar); (** * Read a [[string]] with a maximum length from ''inSocket''. * @return the string read. * @exception RANGE_ERROR The parameter ''maxLength'' is negative. * @exception MEMORY_ERROR Not enough memory to represent the result. *) const func string: gets (inout socket: inSocket, in integer: maxLength) is return gets(inSocket.sock, maxLength, inSocket.bufferChar); (** * Read a word from ''inSocket''. * Before reading the word it skips spaces and tabs. The function * accepts words ending with " ", "\t", "\n", "\r\n" or [[char#EOF|EOF]]. * The word ending characters are not copied into the [[string]]. * That means that the "\r" of a "\r\n" sequence is silently removed. * When the function is left the inSocket.bufferChar contains ' ', * '\t', '\n' or [[char#EOF|EOF]]. * @return the word read. * @exception MEMORY_ERROR Not enough memory to represent the result. *) const func string: getwd (inout socket: inSocket) is return word_read(inSocket.sock, inSocket.bufferChar); (** * Read a line from 'inSocket'. * The function accepts lines ending with "\n", "\r\n" or [[char#EOF|EOF]]. * The line ending characters are not copied into the [[string]]. That * means that the "\r" of a "\r\n" sequence is silently removed. When * the function is left the inSocket.bufferChar contains '\n' or [[char#EOF|EOF]]. * @return the line read. * @exception MEMORY_ERROR Not enough memory to represent the result. *) const func string: getln (inout socket: inSocket) is return line_read(inSocket.sock, inSocket.bufferChar); (** * Determine the end-of-file indicator. * The end-of-file indicator is set if at least one request to read * from the socket failed. The socket functions ''getc'', ''gets'', * ''getln'' and ''getwd'' indicate the end-of-file situation by * setting ''bufferChar'' to [[char#EOF|EOF]]. * @return TRUE if the end-of-file indicator is set, FALSE otherwise. *) const func boolean: eof (in socket: inSocket) is return inSocket.bufferChar = EOF; (** * Determine if at least one character can be read successfully. * This function allows a socket to be handled like an iterator. * Since ''hasNext'' peeks the next character from the socket * it may block. * @return FALSE if ''getc'' would return [[char#EOF|EOF]], TRUE otherwise. *) const func boolean: hasNext (in socket: inSocket) is return hasNext(inSocket.sock); (** * Determine if at least one character can be read without blocking. * Blocking means that ''getc'' would wait until a character is * received. Blocking can last for a period of unspecified length. * @return TRUE if ''getc'' would not block, FALSE otherwise. *) const func boolean: inputReady (in socket: inSocket) is return inputReady(inSocket.sock, 0, 0);