TCP Server
You can bind one or more outstation instances to a TCP server using an AddressFilter
. Unlike most server protocols, each outstation instance can only
communicate with one client at a time. This is because DNP3 is stateful and maintains event data for a specific master. If you need to support more than
one master connection on the same port, you must preconfigure each master IP address and associate it with a specific outstation instance.
Creating a server
Begin by creating a TCPServer
instance. Keep in mind that creating a server doesn't initiate bind/listen
immediately. Endpoints are specified in the format <address>:<port>
. Addresses may be any specified as any valid IPv4 or IPv6 local endpoint, such as:
127.0.0.1
for localhost only0.0.0.0
for all adapters- The IP address for a particular adapter
- Rust
- C
- C++
- Java
- C#
let server = Server::new_tcp_server(LinkErrorMode::Close, "127.0.0.1:20000".parse()?);
dnp3_outstation_server_t* server = NULL;
dnp3_param_error_t err = dnp3_outstation_server_create_tcp_server(runtime, DNP3_LINK_ERROR_MODE_CLOSE, "127.0.0.1:20000", &server);
// check error
auto server = dnp3::OutstationServer::create_tcp_server(runtime, LinkErrorMode::close, "127.0.0.1:20000");
OutstationServer server = OutstationServer.createTcpServer(runtime, LinkErrorMode.CLOSE, "127.0.0.1:20000");
var server = OutstationServer.CreateTcpServer(runtime, LinkErrorMode.Close, "127.0.0.1:20000");
LinkErrorMode
controls what will happen if a malformed link-layer frame is received. Typically, it will close the socket to prevent transmission of corrupted data. However, you can set this value to Discard
if you want the parser to discard errors and search for the start of the next frame. This behavior is required if you're connecting to a terminal server that bridges TCP to a serial port.
Adding Outstations
You can now associate one or more outstations with the TCPServer
. The TcpServer::addOutstation
method takes all the components discussed in previous sections, followed by an AddressFilter
. This filter determines which masters are allowed to associate with a specific outstation.
If the filter allows the same address(es) as an existing outstation's filter, then TcpServer::addOutstation
will fail.
- Rust
- C
- C++
- Java
- C#
let outstation = server.add_outstation(
get_outstation_config(),
Box::new(ExampleOutstationApplication),
Box::new(ExampleOutstationInformation),
Box::new(ExampleControlHandler),
NullListener::create(),
AddressFilter::Any,
)?;
The terminology differs here between Rust and the bindings. The Rust API's 'add_outstation' method doesn't spawn it onto the runtime.
dnp3_address_filter_t *address_filter = dnp3_address_filter_any();
dnp3_param_error_t err = dnp3_outstation_server_add_outstation(
server,
get_outstation_config(),
get_outstation_application(),
get_outstation_information(),
get_control_handler(),
get_connection_state_listener(),
address_filter,
&outstation
);
dnp3_address_filter_destroy(address_filter);
// check error
auto filter = AddressFilter::any();
auto outstation = server.add_outstation(
get_outstation_config(), std::make_unique<MyOutstationApplication>(), std::make_unique<MyOutstationInformation>(),
std::make_unique<MyControlHandler>(),
connection_state_listener([](ConnectionState state) { std::cout << "ConnectionState: " << to_string(state) << std::endl; }), filter);
// check error
final Outstation outstation =
server.addOutstation(
getOutstationConfig(),
new TestOutstationApplication(),
new TestOutstationInformation(),
new TestControlHandler(),
new TestConnectionStateListener(),
AddressFilter.any());
var outstation = server.AddOutstation(
GetOutstationConfig(),
new TestOutstationApplication(),
new TestOutstationInformation(),
new TestControlHandler(),
new TestConnectionStateListener(),
AddressFilter.Any()
);
The examples above use AddressFilter.any()
which allows any master IP to connect.
If you've already connected a master to the outstation and another matching IP address tries to connect, the existing connection will close. The outstation will then begin a communication session with the new master.
The AddressFilter
constructor accepts IPv4 wildcards (e.g. 192.168.0.*) which are convenient when you want to allow any master IP in a subnet to connect to the outstation.
ConnectionStateListener
The ConnectionStateListener
interface is provided when adding an outstation to the TCP server. It has a single method that informs your code when the outstation accepts a TCP connection.
The listener only fires a connected event when it receives an IP address matching the AddressFilter
. If the outstation that is already connected and processing a connection receives
another matching connection, the listener will fire a disconnected event before firing a connected event.
Binding the Server
Once you've added all the outstations associated with the server, you can then bind the server which will cause it to start accepting connections.
- Rust
- C
- C++
- Java
- C#
// dropping the ServerHandle shuts down the server and outstation(s)
let _server_handle = server.bind().await?;
The terminology differs here between Rust and the bindings.
err = dnp3_outstation_server_bind(server);
// check error
server.bind();
server.bind();
server.Bind();
Binding may fail if the underlying socket bind/listen calls fail, such as when another process is already bound to that port.