Socket, Process, and Stream Input and Output

SenseTalk provides a set of commands for reading and writing sockets, processes, and the standard input and output streams (i.e., stdin, stdout, stderr). Fundamentally, these forms can all be treated as streams of data. The read and write commands include many options to make it easy to read or write any type of data including numbers in many formats, text, and raw binary data. HTTP and XML-RPC messages are also supported directly to further simplify communication with many servers on the internet.

Although some details differ between the different types of streams, the basic process of working with all of them follows the same sequence: open, read and/or write, close. The main exception to this sequence is that the standard input, standard output, and standard error streams don't require opening or closing because they are considered to be open at all times.

Note: The commands and functions that you use to work with sockets, processes, and stream data are similar to the commands and functions you can use for working with files and file system objects. For information about working with files and file systems, see File and Folder Interaction.

The socket input and output commands (open socket, close socket, read from socket, and write … to socket) permit you to open a connection to a socket provided by another process and read and write data through that connection. You cannot seek to a specified location on a socket because a socket is just an open communication channel between two processes.

Similarly, the process input and output commands (open process, close process, read from process, and write … to process) allow you to launch an external process and communicate with it by writing to the standard input and reading from the standard output of that process.

The read and write commands can also be used to read from standard input and to write to the standard output or standard error streams (read from input, write … to output, write … to error).

The openSockets function returns a list of all of the currently open sockets.

The DefaultStringEncoding global property controls the encoding format that is used when reading or writing text from a file or socket.

Open Socket Command

Behavior: The open socket command must be used to open a socket before anything can be read from or written to that socket using the read or write commands. When you are finished with a socket, it should be closed using the close socket command.

Open socket establishes a TCP socket connection to another process (program). The other process may be running on the same computer, or on some other computer on the network. It must already be running, and have registered a socket to which SenseTalk can connect. Once the connection is established, data may be transmitted in either direction in whatever manner both sides understand.

Syntax:

open socketsocketIdentifier

The socketIdentifier must be of the form host:port where host is the name or IP address of the machine, and port is the port number on that machine of the socket to be connected to.

The socketIdentifier may optionally end with a pound sign "#” (or a vertical bar "|”) character followed by an arbitrary number or identifier string. This serves the purpose of allowing you to create multiple identifiers to establish more than one connection to the same host and port, and identify each connection uniquely – just use the appropriate socketIdentifier with the read, write, and close commands to identify the correct connection.

If the socket connection cannot be established within the time specified by the readTimeout global property, an exception will be thrown. Use the openSockets function to get a list of all sockets which are currently open.

Example:

open socket remoteListener

Example:

open socket "192.168.1.4:22"

Example:

open socket "localhost:5900#2"

Open Process Command

Behavior: Launches an external process and opens a connection through which the script may interact with that other process. If all that is needed is to run an external process and receive any output from that process when it completes, the shell function provides a much simpler way to achieve that. The open process mechanism, on the other hand, provides much greater flexibility, allowing the script to conduct complex interactions with another process, or to start a lengthy operation without blocking the script and retrieve the results of that operation at a later point in the script.

Open process launches another process (program) which may be (and most commonly is) a shell through which still other programs may be executed. Once the other process is launched, a connection is established and text may be transmitted in either direction – the script may write to the standard input and read from the standard output of the other process.

If the process cannot be launched, the result function will be set to an exception (or the exception will be thrown, if The ThrowExceptionResults global property is set to true). The openProcesses function can be used to get a list of all processes which are currently open.

Syntax:

open processprocessIdentifier {with optionsoptions}

The processIdentifier should be in the form processPath#identifier where processPath is the full path of the process to run. If processPath is omitted, a shell process will be launched (as specified by the shellCommand global property). The #identifier portion is also optional – it merely serves as a way to make a processIdentifier unique, so that a script can open and interact with multiple processes at once that use the same processPath.

If options is used, it should be a property list that may include any of these properties:

parameters

a list of values to be passed as parameters to the process when it is launched

folder or directory

the current directory where the process will be run

environment

a property list specifying environment variables and their values

Example:

open process preferredShell & "#myshell"

Example:

open process "/bin/sh" with options myOptions

Example:

open process "/usr/local/bin/mysql#2"

Close Socket, Close Process, Close All Commands

Behavior: Closes an socket; or process; or all open files, sockets, or processes. The close all sockets command closes all currently open sockets, regardless of which script or handler opened the socket. This behavior could be potentially problematic if sockets have been opened by other scripts, and are still in use. Use the openSockets() function to get a list of all open sockets. The same applies to the close all processes command.

The close socket command closes a socket that was previously opened with the open socket command. The socketIdentifier should be identical to the identifier used when opening the socket.

The close process command closes a process that was previously opened with the open process command. The processIdentifier should be identical to the identifier used when opening the process.

The close all sockets and close all processes commands can be used to close all of the currently open sockets or processes. SenseTalk automatically closes all open sockets and processes whenever it stops executing your scripts, but it is good practice to for your script to close them when it is done working with them.

Syntax:

close socketsocketIdentifier

close processprocessIdentifier

close all sockets

close all processes

Example:

close all processes -- close all open processes

Example:

close socket "localhost:5900"

Example:

close process "#9"

Read from Socket, Read from Process, Read from Input Commands

Behavior: Reads data (text or numbers) from an open socket or an open process, or from the standard input stream.

Use the read from ... command to read data from a socket, process, or standard input. Data is read into the variable it or into a destination container if specified (using an into clause). When there is no more data to read, the destination container will be empty. Any time less data is read than was requested, the result function will contain a value giving the reason (such as time out).

The read command can read a specified number of characters, words, items, lines, or bytes; can read until a specified delimiter is found; or can read a list of one of several different types of numeric values. Reading begins at the current position. The syntax of the read command is flexible, allowing the various options to be specified in any convenient order.

Note: In order to read from a file, see the Read from File Command.

Syntax:

The syntax of the read from ... command is flexible, allowing the various options to be specified in any convenient order.

readOptions

Source Options: One Required.

from socketsocketIdentifier

from processprocessIdentifier

from [input | stdin]

Quantity Options: Optional. Only one can be used at a time.

until [{theeof | {theend {of {thefile} | terminator]

forquantity {dataType}

[an | quantitydataType

{for} {anhttp {message request response}

{for} {an} [xmlrpc xml-rpc] {message request response}

Other Options: Optional. More than one can be used at a time.

at startPos

in | [timeout time out] {after in} ] timeoutDuration

into container

One of the three from options is required, to specify the source from which to read. All other options are optional, but only one of each type may be specified. If neither a for nor until option is given, a single byte is read. If into container isn't specified, the value will be read into the special it variable.

Tech Talk

You do not need to open the standard input stream – it is always open (you can refer to it as stdin instead of input if you prefer).

When reading from a socket, the socketIdentifier expression must yield the exact identifier used when a socket was previously opened with the open socket command.

When reading from a process, the processIdentifier expression must yield the exact identifier used when a process was previously opened with the open process command. The value that is read corresponds to the standard output from the process.

If into container is specified, the data that is read is put into the given container. If an into option is not specified, the data is read into the special it variable.

If until terminator is specified, all of the characters from the starting position until the next occurrence of the specified character or string will be read. This is useful for reading one line at a time from the source (by using return as the terminating character), or to read just until some other delimiting character (such as a tab). The terminator can be more than one character in length, and will be returned as part of the value that was read. Specifying until eof or until end will read all the way to the end of the file, or to the end of input from a socket or stream. The standard input stream indicates it is at the end after a Control-D character is received. For sockets and processes, the until eof or until end option will wait until either some input is available, or the duration of the readTimeout or in timeLimit (see below) has elapsed. This greatly simplifies reading when some input is expected.

If for quantitydataType is used, the number of characters or other data elements specified by quantity are read from the file. If dataType is a text chunk type (characters, words, items, or lines), text is read until the requested amount is available. The final delimiter (if any) is not included with the text that is read. If no dataType is given, bytes are assumed (and the word for is required in this case).

The in timeoutDuration option gives the maximum time the read command will wait for the requested data to become available. If a time is not specified, the value of the readTimeout global property will be used instead. If the requested data is not read within the time specified by timeLimit or readTimeout, whatever has been read will be returned and the result function will be set to indicate time out.

If you specify a numeric dataType instead of a text chunk type, the value stored into it or container by the read will be a list of the data values that were read. The following numeric data types may be used:

DataType

Value

int1 or 8-bit integer

an 8-bit (or 1 byte) signed integer

uint1 or unsigned 8-bit integer

an 8-bit (or 1 byte) unsigned integer

int2 or 16-bit integer or short integer

a 16-bit (or 2 byte) signed integer

uint2 or unsigned 16-bit integer

a 16-bit (or 2 byte) unsigned integer

int4 or 32-bit integer or integer

a 32-bit (or 4 byte) signed integer

uint4 or unsigned 32-bit integer

a 32-bit (or 4 byte) unsigned integer

int8 or 64-bit integer

a 64-bit (or 8 byte) signed integer

uint8 or unsigned 64-bit integer

a 64-bit (or 8 byte) unsigned integer

real4 or 32-bit real or float

a 32-bit (single-precision) floating-point number

real8 or 64-bit real or double

a 64-bit (double-precision) floating-point number

Example:

read from input until return-- read text typed by user

Example:

read from process mysql until end -- read available text

Example:

read into numList from socket inStream for3 unsigned integers

Example:

read 6 unsigned 8-bit integers from socket rfb into unitSales

Example:

read 10 chars from socket "192.168.1.4:22" in 15 seconds

Sending and Receiving HTTP Messages

When a socket is used for communication using the HTTP protocol, SenseTalk provides support for directly reading and writing HTTP messages. To read a message, use one of these commands (or a variation):

read an HTTP message from socket clientSock
read from socket myHTTP for an HTTP request
read an HTTP response into theReply from socket remoteService

The value read will be a property list with all of the information contained in the HTTP message that was read, including Method, Path, and Header properties, similar to this:

(Header:(Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8”, "Accept-Encoding”:"gzip, deflate”, "Accept-Language”:"en-us”, Connection:"keep-alive”, Host:"localhost:5991”, "User-Agent”:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, like Gecko) Version/5.1.1 Safari/534.51.22”), HTTPMessageType:"Request”, Method:"GET”, Path:"/”, Version:"HTTP/1.1”)

Similarly, an HTTP message can be sent across a socket using the write command. To write an HTTP message from a property list, use the HTTPMessage() function or as http operator to convert the property list to an HTTP message and use the write command to write that to the socket:

write HTTPMessage(status:200, body:htmlContents) to socket webClient

write replyMsg as http to socket customerSocket

The HTTP message to be sent should be in the form of a property list that can include these properties:

HTTP Request Message

  • Method: GET or POST (if omitted, GET is assumed if no Body is supplied, and POST if a Body is supplied);
  • Request: Path to the resource being requested on the server (required);
  • Version: If not supplied, HTTP/1.1 is assumed;
  • Header: If given, must be a property list of header keys and values;
  • Body: The body of the message. The body might be text, or it might be a property list, in which case it is automatically encoded in key/value pairs.

HTTP Response Message

  • Version: If not supplied, HTTP/1.1 is assumed;
  • Status: A numeric status value to send. If not specified, defaults to 200;
  • Reason: Text of the reason for the returned status; defaults to OK;
  • Body: The content being sent. For a web server, this is typically the HTML code of a page. The body might be a property list, in which case it is automatically encoded in key/value pairs.

Related:

Write to Sockets, Write to Process, Write to Output, Write to Error Commands

Behavior: Writes data to a socket or process, or to the standard output or standard error stream. Data can be any valid SenseTalk expression. If dataType is not specified, the value of the data expression is treated as a string of characters, which is written out to the specified socket or stream.

The socketIdentifier expression must yield the identifier of a socket that was previously opened with the open socket command.

The processIdentifier expression must yield the identifier of a process that was previously opened with the open process command. The data that is written will be sent to the standard input of the process.

If as dataType is specified, the data is converted to that binary format before being written. In this case, data can be a list of numeric values, which are all converted to the same data type. See the read command for a list of the valid data types.

Syntax:

write data {as dataType} to socket socketIdentifier

write data {as dataType} to processprocessIdentifier

write data {as dataType} to [output | stdout | error | stderr

Example:

write"ls -l"&returnto process"#4"

Example:

write"Please enter your account id: "to output

Example:

write[2,3,5,9] as 8-bit integers to socketmsock

OpenSockets Function

Behavior: Returns a list of all sockets that are currently open as a result of the open socket command.

Syntax:

openSockets()

the openSockets

Example:

repeatwitheach itemof the openSockets

ifitbegins with"192.168.1.12:"thenclose socketit

end repeat

OpenProcesses Function

Behavior: Returns a list of all processes that are currently open and available for interaction as a result of the open process command.

Syntax:

openProcesses()

the openProcesses

Example:

repeatwitheach itemof the openProcesses

ifitbegins with"/usr/bin/ssh"then

write"logout"&returnto processit

end if

end repeat

Sending and Receiving XML-RPC Messages with SenseTalk

To send an XML-RPC request, the post xmlrpc command is used:

set myRequest to (methodName:"system.listMethods")

post xmlrpc myRequest to url serverURL

The message being posted must be a property list with a MethodName property and optionally Params and Header properties:

  • MethodName: Required. The method name being called;
  • Params: An array of parameter values. Specify "as boolean", "as text", "as date", etc., when needed to ensure individual values are encoded as the correct type for the method being called;
  • Header: Optional. A property list of other header elements;
  • User-Agent: if not specified in the Header property, the SenseTalk long version will be supplied.

The URL given in the post command may include a path to identify a particular service on the server. The response from the server will be stored in the variable it.

To send an XML-RPC response, use either the XMLRPCResponse() function or the XMLRPCFault() function, along with the write command:

write XMLRPCResponse(responseData) to socket rpcClient

write XMLRPCFault(33, "bad mojo") to socket rpcClient

The XMLRPCResponse() function

Takes one or two parameters. The first parameter is the value being returned in the response and is required. It may be any type of value, including a list or property list. The second parameter is optional. If supplied it must be a property list containing any additional HTTP headers to be included in the message.

The XMLRPCFault() function

Requires at least two parameters: an integer fault code, and a fault string. A third optional parameter, if it is included, must be a property list containing any additional HTTP headers to be included in the message.

To receive an XML-RPC message on a socket, use the read command and specify xmlrpc or xmlrpc message (to read any type of XML-RPC message), xmlrpc request (will throw an exception if the message received is not a request), or xmlrpc response (will throw an exception if the message received is not a response or fault) as the type of value to read:

read an xmlrpc message from socket clientSocket into msgRecvd

read an xmlrpc request from socket clientSocket

The following keys are recognized:

  • Request: The URI to post to;
  • Host: The host posted to;
  • URL: The URL posted to, including host and request URI;
    Important: Either the Host or URL is required. If Host is given with no Request, the request is set to "/".
  • User-Agent: If not specified, the SenseTalk long version will be supplied;
  • Header: Optional. Other header elements;
  • Method: Required. The method name being called;
  • Params: An array of parameter values. Specify "as boolean", "as text", etc. when needed.

XMLRPCFormat() function

Returns a string representing any value in the format required for use within the body of an XML-RPC message.

XMLContentEncode() function

Converts text into a safe format for including in the content of an XML document by replacing any occurrences of the characters "<" , ">", "&", and single and double quotes with their respective XML entity values.

XMLContentDecode() function

Decodes a string containing certain XML entity values, such as those generated by XMLContentEncode(), and returns the original text.

 

This topic was last updated on August 19, 2021, at 03:30:51 PM.

Eggplant icon Eggplantsoftware.com | Documentation Home | User Forums | Support | Copyright © 2022 Eggplant