<Chapter Label="ClientFunctionality">
<Heading>Client's functionality</Heading>    

Sending and getting requests to the &SCSCP; server(s),
the client operates with processes. Process is an abstraction 
which in other words may be also called a remote task. It encapsulates
an input/output TCP stream (see <Ref Filt="IsInputOutputTCPStream" />) 
from the client to the server and the process ID of the CAS running 
as a server (deduced from the connection initiation message; may be 
unassigned, if the server CAS did not communicate it).
<P/>
There are two ways to create processes. One of them is to specify
the hostname and port where the &SCSCP; server is running; in this 
case a new input/output TCP stream will be created. Another way is 
first to establish the connection with the &SCSCP; server using
<Ref Func="NewSCSCPconnection" /> and then keep it alive across
multiple remote procedure calls, thus saving time on the DNS lookup 
and connection initiation. This may give a good speedup in computations
with an intensive message exchange. Note that as long as such connection
is open, other &SCSCP; clients will not be able to get through, so
if several clients are interchanging with the &SCSCP; server at the 
same time, they should not block each other with long-lasting connections.


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->

<Section Label="SCSCP Connections">
<Heading>&SCSCP; connections</Heading>

<#Include Label="IsSCSCPconnection"/>
<#Include Label="NewSCSCPconnection"/>
<#Include Label="CloseSCSCPconnection"/>

</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->

<Section Label="Client">
<Heading>Processes</Heading>

<#Include Label="IsProcess"/>
<#Include Label="NewProcess"/>
<#Include Label="CompleteProcess"/>
<#Include Label="TerminateProcess"/>

</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->

<Section Label="All-in-one">
<Heading>All-in-one tool: sending request and getting result</Heading>

<#Include Label="EvaluateBySCSCP"/>

Now we demonstrate the procedure <C>GroupIdentificationService</C>,
also given in the previous chapter:

<Example>
<![CDATA[
gap> G:=SymmetricGroup(4);
Sym( [ 1 .. 4 ] )
gap> gens:=GeneratorsOfGroup(G);
[ (1,2,3,4), (1,2) ]
gap> EvaluateBySCSCP( "GroupIdentificationService", [ gens ],
>                     "localhost", 26133 : debuglevel:=3 ); 
rec( attributes := [ [ "call_id", "localhost:26133:2442:xOilXtnw" ], 
      [ "info_runtime", 4 ], [ "info_memory", 2596114432 ], 
      [ "info_message", "Memory usage for the result is 48 bytes" ] ], 
  object := [ 24, 12 ] )
]]>
</Example>

Service provider may suggest to the client to use a counterpart function

<Example>
<![CDATA[
gap> IdGroupWS := function( G )
>    local H, result;
>    if not IsPermGroup(G) then
>      H:= Image( IsomorphismPermGroup( G ) );
>    else
>      H := G;
>    fi;  
>    result := EvaluateBySCSCP ( "GroupIdentificationService", 
>                [ GeneratorsOfGroup(H) ], "localhost", 26133 );
>    return result.object;
> end;;
]]>
</Example>

which works exactly like <Ref BookName="smallgrp" Func="IdGroup" />:

<Example>
<![CDATA[
gap> G:=DihedralGroup(64);
<pc group of size 64 with 6 generators>
gap> IdGroupWS(G);
[ 64, 52 ]
]]>
</Example>

</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->

<Section Label="BinaryVsXML">
<Heading>Switching between Binary and XML &OpenMath; Encodings</Heading>

<#Include Label="SwitchSCSCPmodeToBinary"/>

For example, let us create a vector over <M>GF(3)</M>:
<Example>
<![CDATA[
gap> x := [ Z(3)^0, Z(3), 0*Z(3) ];
[ Z(3)^0, Z(3), 0*Z(3) ]
]]>
</Example>
The XML &OpenMath; encoding of such objects is quite bulky:
<Example>
<![CDATA[
gap> OMPrint( x );
<OMOBJ xmlns="http://www.openmath.org/OpenMath" version="2.0">
	<OMA>
		<OMS cd="list1" name="list"/>
		<OMA>
			<OMS cd="arith1" name="power"/>
			<OMA>
				<OMS cd="finfield1" name="primitive_element"/>
				<OMI>3</OMI>
			</OMA>
			<OMI>0</OMI>
		</OMA>
		<OMA>
			<OMS cd="arith1" name="power"/>
			<OMA>
				<OMS cd="finfield1" name="primitive_element"/>
				<OMI>3</OMI>
			</OMA>
			<OMI>1</OMI>
		</OMA>
		<OMA>
			<OMS cd="arith1" name="times"/>
			<OMA>
				<OMS cd="finfield1" name="primitive_element"/>
				<OMI>3</OMI>
			</OMA>
			<OMI>0</OMI>
		</OMA>
	</OMA>
</OMOBJ>
gap> Length( OMString(x) );
507
]]>
</Example>
We call the &SCSCP; procedure <C>Identity</C> just to test how this object
may be sent back and forth. The total length of the procedure call message
is 969 symbols:
<Log>
<![CDATA[
gap> SetInfoLevel(InfoSCSCP,3);
gap> EvaluateBySCSCP("Identity",[x],"localhost",26133);
#I  Creating a socket ...
#I  Connecting to a remote socket via TCP/IP ...
#I  Got connection initiation message
#I  <?scscp service_name="GAP" service_version="4.dev" service_id="localhost:2\
6133:42448" scscp_versions="1.0 1.1 1.2 1.3" ?>
#I  Requesting version 1.3 from the server ...
#I  Server confirmed version 1.3 to the client ...
#I  Composing procedure_call message: 
<?scscp start ?>
<OMOBJ xmlns="http://www.openmath.org/OpenMath" version="2.0">
  <OMATTR>
    <OMATP>
      <OMS cd="scscp1" name="call_id"/>
      <OMSTR>localhost:26133:42448:IOs9ZkBU</OMSTR>
      <OMS cd="scscp1" name="option_return_object"/>
      <OMSTR></OMSTR>
    </OMATP>
    <OMA>
      <OMS cd="scscp1" name="procedure_call"/>
      <OMA>
        <OMS cd="scscp_transient_1" name="Identity"/>
          <OMA>
            <OMS cd="list1" name="list"/>
            <OMA>
            <OMS cd="arith1" name="power"/>
            <OMA>
              <OMS cd="finfield1" name="primitive_element"/>
              <OMI>3</OMI>
            </OMA>
            <OMI>0</OMI>
          </OMA>
          <OMA>
            <OMS cd="arith1" name="power"/>
            <OMA>
              <OMS cd="finfield1" name="primitive_element"/>
              <OMI>3</OMI>
            </OMA>
            <OMI>1</OMI>
          </OMA>
          <OMA>
            <OMS cd="arith1" name="times"/>
            <OMA>
              <OMS cd="finfield1" name="primitive_element"/>
              <OMI>3</OMI>
            </OMA>
            <OMI>0</OMI>
          </OMA>
        </OMA>
      </OMA>
    </OMA>
  </OMATTR>
</OMOBJ>
<?scscp end ?>
#I  Total length 969 characters 
...
rec( attributes := [ [ "call_id", "localhost:26133:42448:IOs9ZkBU" ] ], 
  object := [ Z(3)^0, Z(3), 0*Z(3) ] )
  ]]>
</Log>
Now we switch to binary mode:
<Log>
<![CDATA[
gap> SwitchSCSCPmodeToBinary();
gap> EvaluateBySCSCP("Identity",[x],"localhost",26133);
#I  Creating a socket ...
#I  Connecting to a remote socket via TCP/IP ...
#I  Got connection initiation message
#I  <?scscp service_name="GAP" service_version="4.dev" service_id="localhost:2\
6133:42448" scscp_versions="1.0 1.1 1.2 1.3" ?>
#I  Requesting version 1.3 from the server ...
#I  Server confirmed version 1.3 to the client ...
#I  Composing procedure_call message: 
3C3F7363736370207374617274203F3E0A18121408060773637363703163616C6C5F6964061E6C\
6F63616C686F73743A32363133333A34323434383A3256675A5562755A0806147363736370316F\
7074696F6E5F72657475726E5F6F626A6563740600151008060E73637363703170726F63656475\
72655F63616C6C1008110873637363705F7472616E7369656E745F314964656E74697479100805\
046C697374316C69737410080605617269746831706F7765721008091166696E6669656C643170\
72696D69746976655F656C656D656E7401031101001110080605617269746831706F7765721008\
091166696E6669656C64317072696D69746976655F656C656D656E740103110101111008060561\
726974683174696D65731008091166696E6669656C64317072696D69746976655F656C656D656E\
7401031101001111111113193C3F736373637020656E64203F3E0A
#I  Total length 339 bytes 
#I  Request sent ...
#I  Waiting for reply ...
#I  <?scscp start ?>
#I  Got back: object [ Z(3)^0, Z(3), 0*Z(3) ] with attributes 
[ [ "call_id", "localhost:26133:42448:2VgZUbuZ" ] ]
rec( attributes := [ [ "call_id", "localhost:26133:42448:2VgZUbuZ" ] ], 
  object := [ Z(3)^0, Z(3), 0*Z(3) ] )
gap> SetInfoLevel(InfoSCSCP,3);
]]>
</Log>
As we can see, the size of the message is almost three times shorter, and
this is not the limit. Switching to binary &OpenMath; encoding in 
combination with pickling and unpickling from &IO; package (see in the last
Chapter) and special methods for pickling compressed vectors implemented
in the <Package>Cvec</Package> available in &GAP;&nbsp;4.5 allow to 
dramatically reduce the overhead for vectors and matrices over finite
fields, making a roundtrip up to a thousand times faster. 

</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->

<Section Label="Remote">
<Heading>Remote objects</Heading>

The &SCSCP; package introduces new kind of objects - <E>remote objects</E>.
They provide an opportunity to manipulate with objects on remote services
without their actual transmitting over the network. Remote objects store
the information that allows to access the original object: the server name 
and the port number through which the object can be accessed, and the variable
name under which it is stored in the remote system. Two remote objects are
equal if and only if all these three parameters coincide.
<P/>

There are two types of remote object which differ by their lifetime: 
<List>
<Item>temporary remote objects which exist only within a single session;</Item>
<Item>persistent remote objects which stay alive across multiple sessions.</Item>
</List>

First we show the example of the temporary remote object in a session. The
procedure <C>PointImages</C> returns the set of images of a point <M>i</M>
under the generators of the group <M>G</M>. First we create the symmetric
group <M>S_3</M> on the client and store it remotely on the server 
(call 1), then we compute set of images for <M>i=1,2</M> (calls 2,3) and 
finally demonstrate that we may retrieve the group from the server (call 4):

<Example>
<![CDATA[
gap> stream:=InputOutputTCPStream( "localhost", 26133 );
< input/output TCP stream to localhost:26133 >
gap> StartSCSCPsession(stream);
"localhost:26133:6184"
gap> OMPutProcedureCall( stream, "store_session", 
>       rec( object := [ SymmetricGroup(3) ], 
>        attributes := [ [ "call_id", "1" ], 
>                        ["option_return_cookie"] ] ) );
true
gap> SCSCPwait( stream );
gap> G:=OMGetObjectWithAttributes( stream ).object;
< remote object scscp://localhost:26133/TEMPVarSCSCPo3Bc8J75 >
gap> OMPutProcedureCall( stream, "PointImages", 
>       rec( object := [ G, 1 ], 
>        attributes := [ [ "call_id", "2" ] ] ) );
true
gap> SCSCPwait( stream );
gap> OMGetObjectWithAttributes( stream );
rec( attributes := [ [ "call_id", "2" ] ], object := [ 2 ] )
gap> OMPutProcedureCall( stream, "PointImages", 
>       rec( object := [ G, 2 ], 
>        attributes := [ [ "call_id", "3" ] ] ) );
true
gap> SCSCPwait( stream );
gap> OMGetObjectWithAttributes( stream );
rec( attributes := [ [ "call_id", "3" ] ], object := [ 1, 3 ] )
gap> OMPutProcedureCall( stream, "retrieve", 
>       rec( object := [ G ], 
>        attributes := [ [ "call_id", "4" ] ] ) );
true
gap> SCSCPwait( stream );
gap> OMGetObjectWithAttributes( stream );
rec( attributes := [ [ "call_id", "4" ] ], 
  object := Group([ (1,2,3), (1,2) ]) )
gap> CloseStream(stream);
]]>
</Example>

After the stream is closed, it is no longer possible to retrieve the
group <M>G</M> again or use it as an argument.
<P/>

Thus, the usage of remote objects existing during a session reduces the
network traffic, since we pass only references instead of actual &OpenMath;
representation of an object. Also, the remote object on the server may
accumulate certain information in its properties and attributes, which may
not be included in it default &OpenMath; representation.
<P/>

Now we show remote objects which remain alive after the session is
closed. Such remote objects may be accessed later, for example, by:
<List>
<Item>subsequent procedure calls from the same instance of &GAP; or another system;</Item>
<Item>other instances of &GAP; or another systems (if the identifier of an object is known)</Item>
<Item>another &SCSCP; servers which obtained a reference to such object as an argument of a procedure call.</Item>
</List>

<#Include Label="StoreAsRemoteObject"/>

Internally, the remote object carries all the information which is required 
to get access to the original object: its identifier, server and port:

<Example>
<![CDATA[
gap> s![1]; 
"TEMPVarSCSCPLvIUUtL3"
gap> s![2];
"localhost"
gap> s![3];
26133
]]>
</Example>

When the remote object is printed in the &OpenMath; format, we
use symbols <C>@</C> and <C>:</C> to combine these parameters
in the &OpenMath; reference:

<Example>
<![CDATA[
gap> OMPrint(s);
<OMOBJ>
      <OMR href="scscp://localhost:26133/TEMPVarSCSCPLvIUUtL3" />
</OMOBJ>
]]>
</Example>

This allows substitution of remote object as arguments into procedure calls 
in the same manner like we do this with usual objects:

<Example>
<![CDATA[
gap> EvaluateBySCSCP("WS_IdGroup",[s],"localhost",26133);  
rec( attributes := [ [ "call_id", "localhost:26133:52918:Viq6EWBP" ] ], 
  object := [ 6, 1 ] )
]]>
</Example>

<#Include Label="IsRemoteObject"/>
<#Include Label="RemoteObjectsFamily"/>
<#Include Label="RetrieveRemoteObject"/>
<#Include Label="UnbindRemoteObject"/>

Finally, we show an example when first we create a group on the service
running on port 26133, and then identify it on the service running on port
26134:
<Example>
<![CDATA[
gap> s:=StoreAsRemoteObject( SymmetricGroup(3), "localhost", 26133 );
< remote object scscp://localhost:26133/TEMPVarSCSCPNqc8Bkan >
gap> EvaluateBySCSCP( "WS_IdGroup", [ s ], "localhost", 26134 );
rec( object := [ 6, 1 ], attributes := [ [ "call_id", "localhost:26134:7414" ] ] )
]]>
</Example>
Instead of transmitting the group to the client and then sending it
as an argument to the second service, the latter service directly 
retrieves the group from the first service:
<Example>
<![CDATA[
gap> EvaluateBySCSCP("WS_IdGroup",[s],"localhost",26133 : output:="cookie" );
rec( attributes := [ [ "call_id", "localhost:26133:52918:mRU6w471" ] ], 
  object := < remote object scscp://localhost:26133/TEMPVarSCSCPS9SVe9PZ > )
]]>
</Example>

</Section>

</Chapter>