This is an old document describing how users of a ScriptEase Javascript/Ecmascript product, or a product that is created with those libraries, can include Distributed Scripting into their application. Javascript users could use this protocol following the Distributed Scripting Protocol manual.

SE:DSP was a technology created by no-longer-existing Nombas.

For more documentation, see Old Nombas User Docs.


DSP Link Library

Distributed Scripting Protocol is implemented by the ScriptEase DSP link library as the DSP object.

DSP Object

platform: All platforms except Dos; All versions of SE

source: #link <sedsp>

The DSP object provides a framework for implementing distributed scripting across a variety of computers and networks.

Creating a DSP object

The Distributed Scripting Protocol provides no internal method for managing a connection or transporting packets. It is simply a framework, with the physical transport method being supplied by the user. As such, it is impossible to simply create a DSP object, because it is incapable of doing anything by itself. The user must supply a set of functions to manage the connection with the server. To create a DSP object, you call new DSP(myOpenFunction, myParameters). The function that you supply must open the connection and return a reference to it. It is possible in some instances that you do not need to open anything special, and so you can ignore this parameter. Here is an example of an open function for a DSP connection, using internet sockets:

function idspOpen( host, port )

{

return new Socket( host, port );

}

We will see this function passed to the DSP constructor in a moment. First, to accomplish sending/receiving packets, the user needs to define two functions, dspSend and dspReceive. These functions must be inherited through the prototype chain, because otherwise when DSP objects are copied implicitly through reference construction (see below), the functions will not get passed. Because we want to keep the DSP functions (such as dspService), we need to preserve the original DSP prototype, and a constructor looks like the following:

function iDSP( host, port )

{

var ret = new DSP( idspOpen, host, port );

// Now we override the ._prototype to insert our functions

if( ret != null )

ret._prototype = iDSP.prototype;

return ret;

}

// Here we set up the iDSP.prototype to keep the DSP functions

// in the chain

iDSP.prototype._prototype = DSP.prototype;

Once this constructor is called, we have a valid DSP object, assuming we add the transport functions. To do this, we must add dspSend and dspReceive to the prototype. The actual syntax of these functions is similar to Clib.fread() and Clib.fwrite(), and a description can be found in the function reference. For our iDSP example, they would look something like this:

function iDSP.prototype.dspSend( conn, buffer, timeout )

{ // Ignore timeout

return conn.write(buffer);

}

function iDSP.prototype.dspReceive( conn, &buffer, length, timeout)

{

return conn.read( buffer, length );

}

Note that both these functions ignore the timeout parameter and do not correctly handle errors. A full-featured version of these functions can be found in the file idsp.jsh. The final function that we must provide is the dspCloseConnection function, which is responsible for closing the connection. This function looks like the following:

function iDSP.prototype.dspCloseConnection( conn )

{

conn.close();

}

Once all of these transport functions have been defined, new iDSP objects can be instantiated with a call to new iDSP and used as any other DSP object. Because the transport level of DSP is separate from the core library, DSP can be adapted to communicate between any servers in any way. In addition, communication can be done during the call to the open function. This allows for password authentication or any other information to be shared.

Using a DSP object

Once a DSP object is created using the method described above, every DSP object behaves in exactly the same way. Once the functions are set up, the transport layer of the protocol is hidden.

The basic idea is that all DSP objects are in fact references to objects on the remote side, and they will remain so except under certain circumstances (described below). When a connection is first established, it is a reference to the global object. Members of the remote global object can be accessed as members of the connection. But they remain references, so var print = connection.Clib.printf will not actually make a remote call to the server. At the appropriate time, print will be resolved into Clib.printf() and sent to the server in the appropriate manner. The circumstances which can trigger a de-referencing and remote call are:

Calling functions - When a DSP reference is called as a function, it gets resolved into the appropriate path and the function is called on the remote server. All parameters are converted to source with ToSource() and passed to the server, and set back afterwards (in case any were passed by reference). The client waits for the return value from the server and returns that as the result of the function call. This makes calling functions transparent to the client, so connection.Screen.writeln("hi") will actually call Screen.writeln on the server and print out "hi".

Setting a value - When a value is put to a DSP reference, such as connection.globalCount = 5, a remote call to the server is generated, and the remote value is updated. The above case acts just as if globalCount = 5 was executed on the server.

Implicitly - When a DSP reference is converted to a primitive, then it gets de-referenced. This implicit conversion happens mostly in operator expressions, in which both values are converted to primitives first. So var myCount = connection.globalCount + 1 will get the value of globalCount from the server and add one to it. This can also be accomplished explicitly with ToPrimitive(), but the method below is more straightforward and understandable. The explicit use of ToPrimitive() on DSP references is discouraged.

Explicitly - Any DSP reference can be explicitly de-referenced with a call to.dspGetValue. Once an object has been de-referenced this way, any subsequent accesses will not cause a remote call, and changes will only affect the local copy. Note that calling a function in this way will result in the function being called on the local client, not the server.

DSP object instance methods

DSP()

syntax:

new DSP( [openFunction[, param1[, ...]]])

where:

openFunction - The function to call to initialize the connection.

paramN - Additional parameters to pass to the open function

return:

object - A new DSP object, or null on error. This is the object that will be passed as the first parameter to all for most of the DSP methods (dspReceive, dspSend). Those methods should use the first passed parameter, and not the "this" variable, for any connection-specific properties or methods--because the dsp object is acting as a proxy to another system to use the "this" variable would instead be acting on the remote system.

description:

This function creates a new DSP object, or returns null on error. Note that calling this function itself accomplishes very little unless you build up an appropriate DSP object by adding open, close, and transport functions. A new DSP object can be created with just new DSP(), but it will be unusable without transport functions. See the introduction, under creating a DSP object, for more information about setting up a proper DSP object. The first optional parameter is the open function to use. Once the object has been created, this function is called with any additional parameters passed to DSP(). The result of this call is set the dspConnection member of the newly created object, and is only used to pass as the first parameters to the dspSend, dspReceive, and dspCloseConnection methods. If openFunction is supplied and returns null, then it is considered an error and the DSP construction fails.

see:

#link <sedsp>

example:

function fileOpen( filename )

{

return Clib.fopen( filename, "wb" );

}

var connection = new DSP( fileOpen, "c:\tempfile.dat" );

// This will call fileOpen and assign the result to

// connection.dspConnection. If it was null,

// then the DSP connection will fail

DSP dspCloseConnection()

syntax:

dsp.closeConnection(connection)

where:

connection - The original connection that was created with the openFunction passed to new DSP()

return:

void.

description:

This function is responsible for terminating the connection that was opened at the time the DSP object was created. This is an optional function, and if not supplied then nothing will be done with the connection. See the introduction, under creating a DSP object, for an example of how to implement this function.

see:

#link <sedsp>, DSP()

DSP dspReceive()

syntax:

dsp.dspReceive(connection, buffer, bufferLength,

timeout)

where:

connection - The original connection that was returned from the openFunction passed to new DSP()

buffer - A buffer which is to be filled with data. This variable must be passed by reference (with the & operator).

bufferLength - The maximum amount of data to read

timeout - The maximum amount of time to wait (in milliseconds) for data to be ready for reading on the connection

return:

number - The number of bytes read, or -1 on error

description:

This function is responsible for getting data from the connection. This function should wait up to timeout milliseconds for data to be available on the connection. If there is no data available, then this function should return 0. Otherwise, the function should read up to bufferLength bytes from the connection and put the data into buffer. Note that this means that buffer must be passed by reference. If there is some sort of error, then this function should either throw an error, or return -1. See introduction, under creating a DSP object, for an example of how to implement this function. Note that the function need not wait for the entire buffer to be filled, it should read only as much data as is available to be read.

see:

#link <sedsp>, DSP dspSend()

DSP dspSend()

syntax:

dsp.dspSend(connection, buffer, timeout)

where:

connection - The original connection that was returned from the openFunction passed to new DSP()

buffer - The buffer to send

timeout - The maximum amount of time to wait (in milliseconds) for data to be ready for writing on the connection

return:

number - The number of bytes written, or -1 on error

description:

This function is responsible for sending data across the connection (the one returned by the openFunction passed to the DSP constructor). Its behavior is similar to that of dspReceive(). It should wait up until timeout for data to be ready, and then send as much as possible along the connection (up to the length of buffer). If the timeout expires, the function should return 0. If there was some sort of error, then an error should be thrown, or -1 returned. Otherwise, the number of bytes written should be returned. Throwing an error is often more descriptive than the generic failure message. See introduction, under creating a DSP object, for an example of how to implement this function.

see:

#link <sedsp>, DSP dspReceive()

DSP dspLoad()

syntax:

dsp.dspLoad(code)

where:

code - String of code to load on the remote server

return:

void.

description:

This function loads the specified code into the global context on the remote server. Any code that you execute will remain on the remote server. This function is designed to load functions on the remote server so that they may be called by the client. This function does not wait for a return value from the host. As a consequence, remote errors will not be immediately reported. They will be reported next time a client routine (calling a function, getting/putting a value) queries the server. Note that if you wish to execute remote code and get a return value, the global eval() method for the server should be used, although the changes will not be permanent.

see:

#link <sedsp>

example:

function foo() { Screen.writeln("Hello!"); }

// This code will make "foo = new Function(...)"

// to set up the function on the remote server.

connection.dspLoad( "foo = " + ToSource(foo) );

connection.foo();

// foo is now a global function on the server

DSP dspService()

syntax:

dsp.dspService()

return:

boolean - A value indicating whether the connection is still open.

description:

This is the main server-side function. Although it can be used by any DSP object, it is intended to be the server side of the client-server model. When called, it will wait until an incoming packet is received and then service that packet appropriately. The method will return false if the packet received was a close command, in which case the connection has been closed, and an explicit call to dspClose is not necessary. It is designed to be called repeatedly until the connection is closed.

see:

#link <sedsp>

example:

// Assume 'connection' is a valid connection

while( connection.dspService() )

;

// At this point, the connection has been

// successfully closed

DSP dspClose()

syntax:

dsp.dspClose()

return:

void.

description:

This function closes the DSP connection. First, it sends a close command to the remote host, signaling that the connection is closing. It then calls the dspCloseConnection method if it exists, passing the original connection variable returned by the open function when this connection was created.

see:

#link <sedsp>

example:

connection.dspClose();

DSP dspGetValue()

syntax:

dsp.dspGetValue()

return:

variable - remote value of the current DSP reference.

description:

This function provides an explicit way to convert a DSP reference into a value. Such conversion is done automatically when the reference is converted to a primitive, or a value is assigned to a reference. See the introduction, under creating a DSP object, for more information on DSP references and getting remote values.

see:

#link <sedsp>

example:

var reference = connection.globalValue;

var value = connection.globalValue.dspGetValue();

reference = 5; // This will change the remote value

value = 6;

// This will change the local copy, not the remote

DSP dspSecurityInit()

syntax:

dsp.dspSecurityInit(secureVar)

where:

secureVar - private storage for the DSP security. The member 'dsp' is preset to the DSP object. Remember, the DSP object can be seen by the running script, but not the secure variable itself.

return:

void.

description:

The dspSecurityInit function turns on security for a DSP object. This means when the remote client tries to run a script on your machine using DSP, it will be run with your security manager in effect. In the case of DSP, each security function (jseSecurityInit, jseSecurityTerm, and jseSecurityGuard) has an exactly corresponding function, i.e., dspSecurityInit, dspSecurityTerm, and dspSecurityGuard. In the security initialization function, you'll typically select some functions to be allowed, and let all others be vetoed.

see:

#link <sedsp>, DSP dspSecurityTerm(), DSP dspSecurityGuard()

example:

function iDSP.dspSecurityGuard( conn )

{

myfunc.setSecurity(jseSecureAllow);

myotherfunc.setSecurity(jseSecureGuard);

}

DSP dspSecurityTerm()

syntax:

dsp.dspSecurityTerm(secureVar)

where:

secureVar - private storage for the DSP security.

return:

void.

description:

This function is typically not needed, but you can use it to cleanup anything you initialized in the DSP security initialization function.

see:

#link <sedsp>, DSP dspSecurityInit(), DSP dspSecurityGuard()

DSP dspSecurityGuard()

syntax:

dsp.dspSecurityGuard(secureVar, function,

params)

where:

function - the function being called

secureVar - private storage for the DSP security.

params - whatever parameters are passed to the function

return:

void.

description:

If a DSP object is given a dspSecurityGuard function (exactly like any of the other DSP callback functions), when it tries to call any function not part of the script (i.e. one of your functions or a wrapper function), the security guard is called for approval. You must provide a dspSecurityInit for security to be activated. Only those functions the security initialization function marks as guarded will use this function.

see:

#link <sedsp>, DSP dspSecurityInit(), DSP dspSecurityTerm()

DSP object static properties

DSP.remote

syntax:

DSP.remote

description:

This global property of the DSP object is used to make calls back to the remote client from within a function. When the first DSP object in a script is created, this gets assigned to that value. From then on, whenever a packet needs to be serviced, this value is set (and later restored) to the object representing the incoming connection. This allows for multiple connections, and lets the function easily call back the appropriate client. Note that within a dspLoad call, the client does not wait for a response, and so trying to call on the client will yield no result until the server is queried again.

see:

#link <sedsp>

example:

// Assume the client calls this:

serverConn.printRemote("hi");

// And the server side looks like this:

function printRemote( string )

{

DSP.remote.Screen.write( string );

}

// This will print out "hi" on the client machine