Skip to main content

TCP server

Bind one or more outstation instances to a TCP server using an AddressFilter. Unlike most server protocols, each outstation can only communicate with one client at a time because DNP3 maintains event data for a specific master. To support multiple masters on the same port, preconfigure each master IP address and associate it with a specific outstation instance.

Creating a server​

Create a TCPServer instance. Creating a server doesn't initiate bind/listen immediately. Specify endpoints as <address>:<port> where address is any valid IPv4 or IPv6 local endpoint:

  • 127.0.0.1 for localhost only
  • 0.0.0.0 for all adapters
  • The IP address for a particular adapter
let server = Server::new_tcp_server(LinkErrorMode::Close, endpoint);

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.

let outstation = server.add_outstation(
get_outstation_config_from_cli(cli),
Box::new(ExampleOutstationApplication::new()),
Box::new(ExampleOutstationInformation),
Box::new(ExampleControlHandler),
LogListener::create("connection"),
AddressFilter::Any,
)?;

The Rust add_outstation method spawns the outstation onto the Tokio runtime, so it must be called from within the runtime. Use add_outstation_no_spawn if you want to spawn the returned future yourself.

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.

// dropping the ServerHandle shuts down the server and outstation(s)
let _server_handle = server.bind().await?;
note

Binding may fail if the underlying socket bind/listen calls fail, such as when another process is already bound to that port.

The Rust bind method spawns the server onto the Tokio runtime, so it must be called from within the runtime. Use bind_no_spawn if you want to spawn the returned future yourself.