Skip to main content
Version: 23.5

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. Unlike a file connection, you cannot seek to a specified location on a socket, process, or stream because these connections are just an open communication channel between two processes. 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.

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 and openProcesses functions return a list of all of the currently open sockets or processes, respectively.

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 socket socketIdentifier

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 process processIdentifier {with {options} options}

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:

parametersa list of values to be passed as parameters to the process when it is launched
folder or directorythe current directory where the process will be run
environmenta 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 a 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 socket socketIdentifier
close process processIdentifier
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; can read a list of one of several different types of numeric values; or can read an HTTP or XML-RPC message. 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:
read Options

Source Options: One Required.

from socket socketIdentifier
from process processIdentifier
from [input | stdin]

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

until [{theeof | {theend {of {thefile} | terminator]
for quantity {dataType}
[a | an | quantitydataType
{for} {a | anhttp {message | request | response}
{for} {a | 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 quantity dataType 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:

DataTypeValue
int1 or 8-bit integeran 8-bit (or 1 byte) signed integer
uint1 or unsigned 8-bit integeran 8-bit (or 1 byte) unsigned integer
int2 or 16-bit integer or short integera 16-bit (or 2 byte) signed integer
uint2 or unsigned 16-bit integera 16-bit (or 2 byte) unsigned integer
int4 or 32-bit integer or integera 32-bit (or 4 byte) signed integer
uint4 or unsigned 32-bit integera 32-bit (or 4 byte) unsigned integer
int8 or 64-bit integera 64-bit (or 8 byte) signed integer
uint8 or unsigned 64-bit integera 64-bit (or 8 byte) unsigned integer
real4 or 32-bit real or floata 32-bit (single-precision) floating-point number
real8 or 64-bit real or doublea 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 for 3 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

Related:

Write to Socket, 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 as 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 process processIdentifier
write data {as dataType} to [output | stdout | error | stderr]

Example:

write "ls -l" & return to process "#4"

Example:

write "Please enter your account id: " to output

Example:

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

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:

repeat with each item of the openSockets
if it begins with "192.168.1.12:" then close socket it
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:

repeat with each item of the openProcesses
if it begins with "/usr/bin/ssh" then
write "logout" & return to process it
end if
end repeat

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 myHTTPsock 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

HTTPMessage Function

Behavior: Generates a message in HTTP format suitable for transmitting over a socket to an HTTP server or client.

Syntax:
HTTPMessage( messageProperties )
{the} HTTPMessage of messageProperties
messageProperties as http

The messageProperties should be a property list that can include these properties, depending on whether the message is an HTTP Request or an HTTP Response:

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.

Example:

put HTTPMessage(status:200, body:"This is the contents of my HTTP message")

This example displays this message:

HTTP/1.1 200 OK
Content-length: 39

This is the contents of my HTTP message

Sending and Receiving XML-RPC Messages

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 these properties:

  • MethodName: Required. The method name being called.
  • Params: Optional. An array of parameter values. Specify "as boolean", "as string", "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. If a "User-Agent" property is not specified here, 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

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 value read will be a property list with all of the information contained in the XML-RPC message that was received.

XMLRPCResponse Function

Behavior: Generates an XML-RPC response formatted as an HTTP message in a format suitable for transmitting over a socket.

Syntax:
XMLRPCResponse( messageContent {, additionalHeaders} )
{the} XMLRPCResponse of messageContent

The messageContent is the value being returned in the response and is required. It may be any type of value, including a list or property list. If additionalHeaders is supplied it must be a property list containing any additional HTTP headers to be included in the message.

XMLRPCFault Function

Behavior: Generates an XML-RPC fault formatted as an HTTP message in a format suitable for transmitting over a socket.

Syntax:
XMLRPCFault( faultCode , faultString {, additionalHeaders} )

The faultCode should be an integer value that identifies the fault. The faultString should be a string that describes the fault. Both the faultCode and faultString are required. If additionalHeaders is supplied it must be a property list containing any additional HTTP headers to be included in the message.

XMLRPCFormat Function

Behavior: Returns a string representing any value in the format required for use within the body of an XML-RPC message. Specify "as boolean", "as string", "as date", etc., when needed to ensure individual values are encoded as the desired type.

Syntax:
XMLRPCFormat( valueToFormat )
{the} XMLRPCFormat of valueToFormat

Example:

put XMLRPCFormat(7) --> <value><int>7</int></value>
put XMLRPCFormat(7 as text) --> <value><string>7</string></value>

XMLContentEncode Function

Behavior: 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.

Syntax:
XMLContentEncode( valueToEncode )
{the} XMLContentEncode of valueToEncode

XMLContentDecode Function

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

Syntax:
XMLContentDecode( valueToDecode )
{the} XMLContentDecode of valueToDecode