%SYSTEM.Socket
Class %SYSTEM.Socket Extends Help [ System = 4 ]
The %SYSTEM.Socket class provides an interface for multiplexing 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 arrives or incoming data becomes 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 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 listener job accepting incoming connection and pass the connected device to worker job so the worker job could communicate with the remote client. The listener job could pass more than one connected devices to the worker job and the worker job could use Select to handle more than one connected devices.
The example code for listener job:/* Assume we already know the process ID of the worker job 'WorkerPID'. */ /* Note that you could have more than one worker jobs to handle incoming connections. */ S ListenDev="|TCP|1",WorkerPID="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 worker. */ Set Token=$SYSTEM.Socket.Publish(ListenDev,WorkerPID) /* Pass the 'Token' to the worker job through the ^TCPConnect(WorkerPID) nodes. */ Set ^TCPConnect(WorkerPID,$Increment(^TCPConnect(WorkerPID)))=Token /* Wake up the worker job in case it is waiting in $SYSTEM.Socket.Select(). */ Set RC=$SYSTEM.Socket.SelectInterrupt(WorkerPID) /* Oops, the worker 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 worker already called the Import() method(). */ /* Otherwise the Import() call on worker 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 code for worker job: /* Assume the worker job already have a TCP device (FirstDev) passed from listener 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 listener 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 listener 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 error. / / And you need to close this device and remove it from the 'SelectDev' list */ Use Dev Read Data Continue } } ``
Methods
Export
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.
Fork
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.
Import
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.
Publish
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.
Select
ClassMethod Select(DeviceList As %List, Timeout As %Decimal) 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. It can be integer or decimal values, it can be down to 100th seconds. 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.
SelectInterrupt
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.