TNI Socket Interface
(Created page with "TNISocket is a builtin TNILibrary within Trainz which allows TNI plugins to communicate via TCP/IP sockets. Before continuing, it is recommended you first familiarise yoursel...") |
m (Protected "TNI Socket Interface" ([edit=sysop] (indefinite) [move=sysop] (indefinite))) |
||
(8 intermediate revisions by one user not shown) | |||
Line 6: | Line 6: | ||
''NOTE: This page is a work in progress, and reflects technologies which are not yet present in retail versions of Trainz.'' | ''NOTE: This page is a work in progress, and reflects technologies which are not yet present in retail versions of Trainz.'' | ||
+ | |||
==Overview== | ==Overview== | ||
− | TNISocket is a TNILibrary built into Trainz native code. It is entirely thread safe, and may be loaded and called at any time from within any TNI plugin DLL. | + | TNISocket is a TNILibrary built into Trainz native code. It is entirely thread safe, and may be loaded and called at any time from within any TNI plugin DLL, using the [[TNI_Core_Interface#TNIContext|TNIGetContextLibrary]] function. |
+ | |||
+ | TNISocket is intended to be used for the following purposes: | ||
+ | * Communication with networked hardware devices running on the same local network as the Trainz client. Examples include dedicated train-related hardware such as DCC interfaces or cab simulators, or kit-form hardware such as raw electronics interfaces. Devices such as printers, and standard services such as file shares or web sites should not be accessed in this manner. | ||
+ | * Communication with executable processes running on the same machine, or same local network, as the Trainz client. The executable processes should remain usable while Trainz is not running, and Trainz should remain usable while the executable process is not running. This means that an external process which helps efficiently edit a route or session is permitted, but an external process which is required to load or drive a given route or session is not permitted. | ||
+ | |||
+ | The following usages are specifically prohibited: | ||
+ | * Communication between multiple Trainz clients for purposes such as multiplayer chat, gameplay, etc. (You should use the iTrainz network for this purpose.) | ||
+ | * Exposing the TNI to an external process for the primary purpose of avoiding the security and code-review limitations placed on the TNI plugins. In short, if your desired goal can be achieved without relying on an external process, we will reject any TNI proposal for an external process implementation. | ||
+ | * File sharing, web browsing, etc. Use a web browser, there's no need to build this into Trainz. | ||
+ | * Generic socket access. Your TNI proposal must clearly indicate how your plugin will use the TNISocket interface, and you must take reasonable precautions to avoid using the TNISocket interface outside of that specified purpose. | ||
+ | * Excessive activity. Your plugin should perform a reasonable minimum of effort to achieve its goals. Plugins which generate substantial amounts of network traffic without showing a clear requirement will be rejected. This includes metrics such as bytes per second, packets per second, and connections per second. | ||
TNISocket communication is implemented through TNIStream. Certain stream operations, such as TNIStreamTell(), TNIStreamGetSize(), and TNIStreamStoreObjectReference(), are not available on socket streams. All TNIStream operations are blocking, and are interrupted by stream closure. | TNISocket communication is implemented through TNIStream. Certain stream operations, such as TNIStreamTell(), TNIStreamGetSize(), and TNIStreamStoreObjectReference(), are not available on socket streams. All TNIStream operations are blocking, and are interrupted by stream closure. | ||
Line 22: | Line 34: | ||
====OpenRemote==== | ====OpenRemote==== | ||
+ | Open a new socket. This function blocks until the socket connects, the connection attempt fails, or a timeout occurs. It is not possible for the plugin to cancel a connection attempt. | ||
+ | |||
+ | Parameters - a 4 element TNIArray* containing: | ||
+ | * A TNIString* containing an index into the socket templates in the config file | ||
+ | * A TNIString* containing the IPv4 address or domain name to connect to | ||
+ | * A TNIString* containing the TCP port number to connect to | ||
+ | * A TNILibrary* to the calling library | ||
+ | |||
+ | Return value - A 2 element TNIArray* containing: | ||
+ | * A TNIString* containing an empty string on success, or an error label on failure | ||
+ | * A TNIStream* for the newly connected stream on success, or nullptr on failure. The stream is closed when this object is fully released. | ||
====Listen==== | ====Listen==== | ||
+ | Binds a local TCP port to listen for incoming connections. When an incoming connection occurs, a library call is sent to the observer with the new TNIStream* as its parameter. If this call is ignored, the incoming connection is immediately dropped- this will be seen by the remote end as an connection and immediate disconnection, not as a connection failure. | ||
+ | |||
+ | Parameters - a 4 element TNIArray* containing: | ||
+ | * A TNIString* containing an index into the socket templates in the config file | ||
+ | * A TNILibrary* to the calling library | ||
+ | |||
+ | Return value - A 2 element TNIArray* containing: | ||
+ | * A TNIString* containing an empty string on success, or an error label on failure | ||
+ | * TNIObject* - An object representing this listen port. The listen port is closed when this object is fully released. | ||
====Close==== | ====Close==== | ||
+ | Closes a socket. This prevents any further send or receive activity on the socket, and will cancel any outstanding send or receive calls in progress on another thread. The remote endpoint can typically detect closure. Calling this function multiple times is harmless but pointless. Fully releasing the socket object implies automatic closure. | ||
+ | |||
+ | Parameters: | ||
+ | * A TNIStream* for the socket to close | ||
+ | |||
+ | Return value: | ||
+ | * None | ||
====Poll==== | ====Poll==== | ||
+ | Asynchronously queries a socket's status. A single library call is made to the observer when either: | ||
+ | * The requested amount of unread data is present on the socket | ||
+ | * An error is flagged on the socket. | ||
+ | |||
+ | The library call may happen at any time after the Poll function is called, including immediately if the necessary conditions are already met. To receive additional notifications past the first, another call to Poll must be placed. | ||
+ | |||
+ | It is recommended that only a single call to Poll is placed on a given socket at a time. If multiple calls are made to Poll on a single socket before the notification is placed, only the last is honored. This can be problematic however, as there is no way for the caller to know which (or how many) of the Poll calls were honored. The cookie may be used to distinguish between Poll calls in this scenario, however the returned bytecount may be misleading since the notifications may overlap in time. | ||
+ | |||
+ | Parameters - a 4 element TNIArray* containing: | ||
+ | * A TNIStream* for the socket to poll | ||
+ | * A TNIString* containing the number of bytes required to trigger a notification | ||
+ | * A TNILibrary* to the calling library | ||
+ | * A TNIObject* which is not used by the Poll function itself, but will be passed to the observer notification function when the poll conditions are met | ||
+ | |||
+ | Return value: | ||
+ | * None | ||
====NotifyConnection==== | ====NotifyConnection==== | ||
+ | Notifies an observer of a new connection after a previous call to the Listen function. If this call is ignored, the incoming connection is immediately dropped- this will be seen by the remote end as an connection and immediate disconnection, not as a connection failure. | ||
+ | |||
+ | Parameters: | ||
+ | * A TNIStream* for the newly opened connection | ||
+ | |||
+ | Expected return value: | ||
+ | * None | ||
====NotifyPoll==== | ====NotifyPoll==== | ||
+ | Notifies an observer that the requested conditions have been met for a previous call to Poll. This will be called once per call to Poll, to receive additional notifications another call to Poll must be placed. | ||
+ | |||
+ | Parameters: | ||
+ | * The TNIObject* passed to the Poll function | ||
+ | |||
+ | Expected return value: | ||
+ | * None |
Latest revision as of 10:25, 12 December 2017
TNISocket is a builtin TNILibrary within Trainz which allows TNI plugins to communicate via TCP/IP sockets.
Before continuing, it is recommended you first familiarise yourself with the following pages:
- TrainzNativeInterface - For an overview of TNI and the associated licensing requirements
- TNI Core Interface - For details of the core TNI functionality, object types, etc.
NOTE: This page is a work in progress, and reflects technologies which are not yet present in retail versions of Trainz.
Contents |
[edit] Overview
TNISocket is a TNILibrary built into Trainz native code. It is entirely thread safe, and may be loaded and called at any time from within any TNI plugin DLL, using the TNIGetContextLibrary function.
TNISocket is intended to be used for the following purposes:
- Communication with networked hardware devices running on the same local network as the Trainz client. Examples include dedicated train-related hardware such as DCC interfaces or cab simulators, or kit-form hardware such as raw electronics interfaces. Devices such as printers, and standard services such as file shares or web sites should not be accessed in this manner.
- Communication with executable processes running on the same machine, or same local network, as the Trainz client. The executable processes should remain usable while Trainz is not running, and Trainz should remain usable while the executable process is not running. This means that an external process which helps efficiently edit a route or session is permitted, but an external process which is required to load or drive a given route or session is not permitted.
The following usages are specifically prohibited:
- Communication between multiple Trainz clients for purposes such as multiplayer chat, gameplay, etc. (You should use the iTrainz network for this purpose.)
- Exposing the TNI to an external process for the primary purpose of avoiding the security and code-review limitations placed on the TNI plugins. In short, if your desired goal can be achieved without relying on an external process, we will reject any TNI proposal for an external process implementation.
- File sharing, web browsing, etc. Use a web browser, there's no need to build this into Trainz.
- Generic socket access. Your TNI proposal must clearly indicate how your plugin will use the TNISocket interface, and you must take reasonable precautions to avoid using the TNISocket interface outside of that specified purpose.
- Excessive activity. Your plugin should perform a reasonable minimum of effort to achieve its goals. Plugins which generate substantial amounts of network traffic without showing a clear requirement will be rejected. This includes metrics such as bytes per second, packets per second, and connections per second.
TNISocket communication is implemented through TNIStream. Certain stream operations, such as TNIStreamTell(), TNIStreamGetSize(), and TNIStreamStoreObjectReference(), are not available on socket streams. All TNIStream operations are blocking, and are interrupted by stream closure.
As TNISocket is a blocking interface, any call made will block the calling thread until it completes or fails. Care must be taken to ensure that your TNI Plugin does not become unresponsive - this will not affect the Trainz client but may affect your plugin's user experience and may be cause for rejection of your plugin. The simplest approach is to spawn a new thread per socket, ensuring that delays on a socket do not lead to delays on your TNI Plugin's librarycall interface. Since most TNI plugins will only work with one socket, this is not expected to cause any difficulties or performance concerns.
All data is encoded as raw binary with no additional protocol. Text strings are sent as a 32-bit length followed by raw UTF8. AssetIDs do not yet have a standardised format and should not be used. Raw data bytes may be sent using TNIStreamWriteInt8Array().
Text-based protocols can be implemented by building the desired string and sending it via TNIStreamWriteInt8Array(), and by reading character-at-a-time using TNIStreamReadInt8() until encountering the appropriate CR/LF sequence.
[edit] TNISocket Library Calls
[edit] OpenRemote
Open a new socket. This function blocks until the socket connects, the connection attempt fails, or a timeout occurs. It is not possible for the plugin to cancel a connection attempt.
Parameters - a 4 element TNIArray* containing:
- A TNIString* containing an index into the socket templates in the config file
- A TNIString* containing the IPv4 address or domain name to connect to
- A TNIString* containing the TCP port number to connect to
- A TNILibrary* to the calling library
Return value - A 2 element TNIArray* containing:
- A TNIString* containing an empty string on success, or an error label on failure
- A TNIStream* for the newly connected stream on success, or nullptr on failure. The stream is closed when this object is fully released.
[edit] Listen
Binds a local TCP port to listen for incoming connections. When an incoming connection occurs, a library call is sent to the observer with the new TNIStream* as its parameter. If this call is ignored, the incoming connection is immediately dropped- this will be seen by the remote end as an connection and immediate disconnection, not as a connection failure.
Parameters - a 4 element TNIArray* containing:
- A TNIString* containing an index into the socket templates in the config file
- A TNILibrary* to the calling library
Return value - A 2 element TNIArray* containing:
- A TNIString* containing an empty string on success, or an error label on failure
- TNIObject* - An object representing this listen port. The listen port is closed when this object is fully released.
[edit] Close
Closes a socket. This prevents any further send or receive activity on the socket, and will cancel any outstanding send or receive calls in progress on another thread. The remote endpoint can typically detect closure. Calling this function multiple times is harmless but pointless. Fully releasing the socket object implies automatic closure.
Parameters:
- A TNIStream* for the socket to close
Return value:
- None
[edit] Poll
Asynchronously queries a socket's status. A single library call is made to the observer when either:
- The requested amount of unread data is present on the socket
- An error is flagged on the socket.
The library call may happen at any time after the Poll function is called, including immediately if the necessary conditions are already met. To receive additional notifications past the first, another call to Poll must be placed.
It is recommended that only a single call to Poll is placed on a given socket at a time. If multiple calls are made to Poll on a single socket before the notification is placed, only the last is honored. This can be problematic however, as there is no way for the caller to know which (or how many) of the Poll calls were honored. The cookie may be used to distinguish between Poll calls in this scenario, however the returned bytecount may be misleading since the notifications may overlap in time.
Parameters - a 4 element TNIArray* containing:
- A TNIStream* for the socket to poll
- A TNIString* containing the number of bytes required to trigger a notification
- A TNILibrary* to the calling library
- A TNIObject* which is not used by the Poll function itself, but will be passed to the observer notification function when the poll conditions are met
Return value:
- None
[edit] NotifyConnection
Notifies an observer of a new connection after a previous call to the Listen function. If this call is ignored, the incoming connection is immediately dropped- this will be seen by the remote end as an connection and immediate disconnection, not as a connection failure.
Parameters:
- A TNIStream* for the newly opened connection
Expected return value:
- None
[edit] NotifyPoll
Notifies an observer that the requested conditions have been met for a previous call to Poll. This will be called once per call to Poll, to receive additional notifications another call to Poll must be placed.
Parameters:
- The TNIObject* passed to the Poll function
Expected return value:
- None