Skip to main content

%SYSTEM.Socket

class %SYSTEM.Socket extends %SYSTEM.Help

The %SYSTEM.Socket class provides an interface for multiplexing Cache TCP devices.

You could use Fork() and Select() methods to have one job handle accepting new connection and reading data from connected TCP device at the same time.
After a listening TCP device receives a connection, we could use Fork() to create a new TCP device for reading data. The original listening TCP device could continue to accept an incoming connection. Then you could use the Select() method to wait for both listening and connected TCP device. When a new connect request arrived or incoming data became available the Select() will return with the device name which was signaled.
Set ListenDev="|TCP|1" Open ListenDev:(:9999:"SA") Set SelectDev=$LB(ListenDev),DevNameIndex=1
  While (1) {
     Set ReturnDev=$SYSTEM.Socket.Select(SelectDev)
     For i=1:1:$ListLength(ReturnDev) {
         Set Dev=$Li(ReturnDev)
         If Dev=ListenDev {
             /* This is the listening device, so it is a new connection. */
             Set ConnectedDev="|TCP|"_$Increment(DevNameIndex)
             If '$SYSTEM.Socket.Fork(Dev,ConnectedDev) {
                 /* Failed to create new device, log error here and continue */
                 Continue
             }
             /* Put this new device in the select list. */
             Set SelectDev=SelectDev_$LB(ConnectedDev)
         } Else {
             /* This is a connected device, so data is available to read. */
             /* Note that if remote closed the connection, this read command would get a <READ> error. */
             /* And you need to close this device and remove it from the 'SelectDev' list */
             Use Dev Read Data
             Continue
         }
     }
  }
  


Use Select(), Publish(), Export() and Import() methods to have a master job accepting incoming connection and pass the connected device to slave job so the slave job could communicate with the remote client. The master job could pass more than one connected devices to the slave job and the slave job could use Select() to handle more than one connected devices.
The example codes for master job:
  /* Assume we already know the process ID of the slave job 'SlavePID'. */
  /* Note that you could have more than one slave jobs to handle incoming connections. */
  S ListenDev="|TCP|1",SlavePID="A Process' PID" Open ListenDev:(:9999:"SA")
  While (1) {
      Use ListenDev Read Data
      /* A new connection came in, create the Token to be passed to the slave. */
      Set Token=$SYSTEM.Socket.Publish(ListenDev,SlavePID)
      /* Pass the 'Token' to the slave job through the ^TCPConnect(SlavePID) nodes. */
      Set ^TCPConnect(SlavePID,$Increment(^TCPConnect(SlavePID)))=Token
      /* Wake up the slave job in case it is waiting in $SYSTEM.Socket.Select(). */
      Set RC=$SYSTEM.Socket.SelectInterrupt(SlavePID)
  		/* Oops, the slave job is gone. Close it and continue. */
      if 'RC {
          /* Disconnect the connection. */
          Use ListenDev Write *-2 
          Continue
      }
      /* An important note for VMS platform, you need to call Export() method */
      /* after you are sure the slave already called the Import() method(). */
      /* Otherwise the Import() call on slave job will fail. */
      Set RC=$SYSTEM.Socket.Export(ListenDev)
  		/* Oops, could not export  the device. Close it and continue. */
      if 'RC {
          /* Disconnect the connection. */
          Use ListenDev Write *-2 
          Continue
      }
  }    
  

The example codes for slave job:
  /* Assume the slave job already have a TCP device (FirstDev) passed from master job through JOB command. */
  Set SelectDev=$LB(FirstDev),DevNameIndex=0,MyPID=$P($J,":")
  While (1) {
     /* Wait for the read to be read with 10 seconds time out. */
     Set ReturnDev=$SYSTEM.Socket.Select(SelectDev,10)
     If ReturnDev="" {
         /* The Select() must be timed out. Do whatever it needs to do then continue to read. */
         Continue
     }
     If ReturnDev=-1 {
         /* The Select() was interrupted, there must be a new connection passed from master job */
         Set Index=$O(^TCPConnect(MyPID,""),1,Token)
         /* If no device in the global then this might be a false alarm, continue. */
         If Index="" Continue
         Set ConnectedDev="|TCP|"_$Increment(DevNameIndex)
         Set RC=$SYSTEM.Socket.Import(ConnectedDev,Token)
         If 'RC {
             /* Failed to import the device, clean up and continue. */
             Kill ^TCPConnect(MyPID,Index)
             Continue
         }
         /* Put this new device in the select list and continue to read. */
         Set SelectDev=SelectDev_$LB(ConnectedDev)
         Kill ^TCPConnect(MyPID,Index)
         /* Important note that for VMS platform, you need to signal master job that */
         /* you have called Import() method so it could call Export(). */
         Continue
     }
     For i=1:1:$ListLength(ReturnDev) {
         Set Dev=$Li(ReturnDev)
         /* This is a connected device, so data is available to read. */
         /* Note that if remote closed the connection, this read command would get <READ> error. */
         /* And you need to close this device and remove it from the 'SelectDev' list */
         Use Dev Read Data
         Continue
     }
  }
  

Method Inventory

Methods

classmethod Export(AcceptedDevice As %String) as %Boolean
Export a socket handle of an accepted TCP device.

Parameters:
AcceptedDevice - The name of a TCP device that has accepted an incoming connection.

Return:
This method returns 1 if succeeds, otherwise returns 0.

Usage:
This method is called by a donor after a published token is passed to the recipient job.
It cleaned up the socket handle in the accepted TCP device so it could go back to accept another incoming connection.
For Windows platform, this method does not interact with the recipient job. For UNIX platform, internally it waits for the recipient job to connect to it through UNIX domain so the socket handle could be passed to the recipient job through it.
For OpenVMS platform, it is needed to make sure the Import() method is called by the recipient job before this method is called, otherwise the Import() will fail on the recipient job.
classmethod Fork(AcceptedDevice As %String, NewDevice As %String) as %Boolean
Create another TCP device from an accepted TCP device within the same job.

Parameters:
AcceptedDevice - The name of a TCP device that has accepted an incoming connection.
NewDevice - The name of a TCP device that is created and a socket handle is passed to it from the AcceptedDevice.

Return:
This method returns 1 if succeeds, otherwise returns 0.

Usage:
Combined with the Select() method, one job could handle more than one incoming TCP connection while accepting new connection to arrive.
The NewDevice must not be opened before this call. After this call the remote end will communicate with the NewDevice.
classmethod Import(NewDevice As %String, Token As %String) as %Boolean
Create a TCP device with a token passed from the donor job.

Parameters:
NewDevice - The name of a TCP device that is created and a socket handle is generated from the Token.
Token - A token passed from the donor job to create a TCP device. It was generated from the Publish() method by the donor job.

Return:
This method returns 1 if succeeds, otherwise returns 0.

Usage:
This method is called by a recipient after it received a published token from the donor job.
It creates the NewDevice TCP device by using the socket handle generated from the Token.
The NewDevice must not be opened before this call. After this call the remote end will communicate with the NewDevice.

For Windows platform, this method does not interact with the donor job.

For UNIX platform, internally it connects to the donor job through UNIX domain to get the socket handle.

For OpenVMS platform, it is needed to make sure the Export() method is called by the donor job after this method is called, otherwise the Import() will fail.
classmethod Publish(AcceptedDevice As %String, RecipientPID As %Integer) as %String
Publish a token from an accepted TCP device.

If the TCP device was opened with the /SSL option and data has been exchanged in this device then this Publish() method will get an error. If there is no data exchanged yet then the Import() on the recipient will open the device with /SSL option implicitly.
Parameters:
AcceptedDevice - The name of a TCP device that has accepted an incoming connection.
RecipientPID - Process ID of the recipient job. Only Windows platform uses this value.

Return:
Return a Token for this accepted TCP device.

Usage:
The Token generated by this Publish() method is passed to a recipient job to create a TCP device to communicate with the remote end.
classmethod Select(DeviceList As %List, Timeout As %Integer) as %List
Check a list of TCP devices whether any devices are ready for read, it includes the listening TCP device.

Parameters:
DeviceList - A list of TCP devices (in $LIST format) to be checked, the devices have to be either connected or in the listening state.
Timeout - A timeout value in seconds for this method to wait. A negative value or omit this parameter will be treated as no timeout.

Return:
Return a list of TCP devices (in $LIST format) that are ready to read.
Return a null string if it is timed out.
Return -1 if it is interrupted by SelectInterrupt()
Usage:
The devices in the list could be a TCP device which has been connected or a TCP device in the listening state and waiting for incoming connection.
When a listening TCP device is returned then this device is ready to receive data from remote end, but there might be no data to read. Internally the accept task is performed.
For a connected TCP device, the next read command should return some data.
The caller should check for null string and -1 for timed out or interruption before you could use the devices returned from the list.
The limit of the total number of TCP device in the list is 64. But for Windows platform one slot is reserved for interrupting the waiting. For a device waiting for accepting an incoming connection it might take two slots if IPV6 is enabled.
classmethod SelectInterrupt(ProcessID As %Integer) as %Integer
Interrupt a process waiting in %SYSTEM.Socket.Select().

Parameters:
ProcessID - The process's PID to be interrupted.

Return:
Return one if succeed, zero if failed.
Usage:
This function is used to make a process break out of Select() method so it could continue to do other works.
If the process is not waiting in Select() method while it is interrupted then when the process calls Select() method it would return null string if it has to wait in Select() method.
The interrupted Select() method could return null string if no device is ready to read data.

Inherited Members

Inherited Methods

FeedbackOpens in a new tab