-
Notifications
You must be signed in to change notification settings - Fork 307
Description
Expected Behaviour
Client.State() should return Disconnected or Reconnecting if the connection to the OPCUA server has been severed.
The channel given to the Client using the option StateChangedCh(ch chan<- opcua.ConnState) should also receive the Disconnected or Reconnecting status.
Disconnected should be returned as soon as the connection has been severed e.g. because the network cable has been removed (simplest case?).
Reconnecting should be returned as soon as the client tries to reconnect to the server.
This is the behaviour I assumed based on the description for the different ConnStates in connstate.go.
Because of this I do not notice that the connection to the OPCUA server has been disconnected and e.g. show the user that the application is still connected to the OPCUA server.
Actual Behaviour
In my test application (code repo) I am using the StateChangedCh(...) option to receive client state changes asynchronously. The code is taken from the monitor example in this repo with the following changes:
- added
StateChangedCh(...)option to the client options - set
AutoConnect(true)option explicitly (even though I think this is default anyways) - added a goroutine receiving and printing the client state value to the terminal
- removed the callback-based subscription (I don't use those in my real application)
- changed the subscription interval to 5s since the default 100ms produced too much output
Connecting and Connected are sent as expected when the client is connecting successfully to the server.
Disconnected and Reconnected are only sent after the underlying connectivity issue is fixed and not as soon as the connectivity issue happens (or at least after some timeout fires).
Steps to Reproduce the Problem
- Clone my code
- Run the application connecting to a standard OPCUA server
state changed: Connectingis output as soon asc.Connect(ctx)is calledstate changed: Connectedis output once the connection has been established- Wait for data changes being output to verify data is received from the OPCUA server
- Sever the connection to the server (e.g. remove your network cable)
- Wait some minutes - no output is printed
- Fix the connection to the server (e.g. plug network cable back in)
state changed: Disconnectedandstate changed: Reconnectingis output in an instantstate changed: Reconnectingis output multiple timesstate changed: Connectedis output once the connection to the server has been reestablished
Logs
I've run the application with --debug to also enable the opcua internal logging. I'll highlight the output from the main application with ===> since the OPCUA debug output is very verbose.
Connecting the first time:
debug: conn.go:70 uacp: connecting to opc.tcp://xxx
===> 2025/09/17 16:07:55 state changed: Connecting
debug: conn.go:94 uacp 2: start HEL/ACK handshake
debug: conn.go:429 uacp 2: sent HELF with 63 bytes
debug: conn.go:388 uacp 2: recv ACKF with 28 bytes
debug: conn.go:271 uacp 2: recv &uacp.Acknowledge{Version:0x0, ReceiveBufSize:0xffff, SendBufSize:0xffff, MaxMessageSize:0x1000000, MaxChunkCount:0x102}
debug: secure_channel.go:576 sc.open
debug: secure_channel_instance.go:89 got sequence number 1
debug: secure_channel.go:1050 uasc 2/1: send *ua.OpenSecureChannelRequest with 132 bytes
debug: conn.go:388 uacp 2: recv OPNF with 135 bytes
debug: secure_channel.go:350 uasc 2/1: recv OPNF with 135 bytes
debug: secure_channel.go:289 uasc 2/1: recv *ua.OpenSecureChannelResponse
debug: secure_channel.go:304 uasc 2/1: sending *ua.OpenSecureChannelResponse to handler
debug: secure_channel.go:660 OpenSecureChannelResponse handler
debug: secure_channel.go:670 sc.handleOpenSecureChannelResponse
debug: secure_channel.go:698 uasc 2: received security token. channelID=29316 tokenID=1 createdAt=2025-09-17T14:07:55Z lifetime=1h0m0s
debug: secure_channel_instance.go:89 got sequence number 2
debug: secure_channel.go:868 uasc 2: security token expires at 2025-09-17T15:22:55Z. channelID=29316 tokenID=1
debug: secure_channel.go:836 uasc 2: security token is refreshed at 2025-09-17T14:52:55Z (45m0s). channelID=29316 tokenID=1
debug: secure_channel.go:1050 uasc 2/2: send *ua.CreateSessionRequest with 270 bytes
debug: conn.go:388 uacp 2: recv MSGF with 12336 bytes
debug: secure_channel.go:350 uasc 2/2: recv MSGF with 12336 bytes
debug: secure_channel.go:289 uasc 2/2: recv *ua.CreateSessionResponse
debug: secure_channel.go:304 uasc 2/2: sending *ua.CreateSessionResponse to handler
debug: secure_channel_instance.go:89 got sequence number 3
debug: secure_channel.go:1050 uasc 2/3: send *ua.ActivateSessionRequest with 141 bytes
debug: conn.go:388 uacp 2: recv MSGF with 96 bytes
debug: secure_channel.go:350 uasc 2/3: recv MSGF with 96 bytes
debug: secure_channel.go:289 uasc 2/3: recv *ua.ActivateSessionResponse
debug: secure_channel.go:304 uasc 2/3: sending *ua.ActivateSessionResponse to handler
===> 2025/09/17 16:07:55 state changed: Connected
debug: client: monitor: start
Wait for some data changes and receive those:
debug: publish: notAcked=[]
debug: publish: notif: 1
debug: publish: pendingAcks=[{"SubscriptionID":1115502,"SequenceNumber":1}]
debug: publish: PublishRequest: {"RequestHeader":null,"SubscriptionAcknowledgements":[{"SubscriptionID":1115502,"SequenceNumber":1}]}
debug: secure_channel_instance.go:89 got sequence number 8
debug: secure_channel.go:1050 uasc 2/8: send *ua.PublishRequest with 106 bytes
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:00Z node=i=2258 value=2025-09-17 14:08:00.6549539 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:00Z node=i=2258 value=2025-09-17 14:08:00.7559604 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:00Z node=i=2258 value=2025-09-17 14:08:00.8569557 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:00Z node=i=2258 value=2025-09-17 14:08:00.9580064 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:01Z node=i=2258 value=2025-09-17 14:08:01.0589656 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:01Z node=i=2258 value=2025-09-17 14:08:01.1600007 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:01Z node=i=2258 value=2025-09-17 14:08:01.2610122 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:01Z node=i=2258 value=2025-09-17 14:08:01.3620195 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:01Z node=i=2258 value=2025-09-17 14:08:01.4630016 +0000 UTC
===> 2025/09/17 16:08:01 [channel ] sub=1115502 ts=2025-09-17T14:08:01Z node=i=2258 value=2025-09-17 14:08:01.5640223 +0000 UTC
Disconnect network cable to simulate connectivity issues that can happen where my application is used - No Output.
Connect network cable and wait some time:
debug: conn.go:388 uacp 2: recv MSGF with 424 bytes
debug: secure_channel.go:350 uasc 2/9: recv MSGF with 424 bytes
debug: secure_channel.go:289 uasc 2/9: recv *ua.PublishResponse
debug: secure_channel.go:304 uasc 2/9: sending *ua.PublishResponse to handler
debug: secure_channel.go:329 uasc 2: readChunk EOF
debug: client: monitor: disconnected
debug: client: monitor: auto-reconnecting
debug: client: monitor: action: createSecureChannel
debug: conn.go:221 uacp 2: close
debug: secure_channel.go:1180 uasc 2: Close()
debug: client: monitor: trying to recreate secure channel
debug: conn.go:70 uacp: connecting to xxx
===> 2025/09/17 16:09:35 state changed: Disconnected
===> 2025/09/17 16:09:35 state changed: Reconnecting
debug: conn.go:94 uacp 3: start HEL/ACK handshake
debug: conn.go:429 uacp 3: sent HELF with 63 bytes
debug: conn.go:388 uacp 3: recv ACKF with 28 bytes
debug: conn.go:271 uacp 3: recv &uacp.Acknowledge{Version:0x0, ReceiveBufSize:0xffff, SendBufSize:0xffff, MaxMessageSize:0x1000000, MaxChunkCount:0x102}
debug: secure_channel.go:576 sc.open
debug: secure_channel_instance.go:89 got sequence number 1
debug: secure_channel.go:1050 uasc 3/1: send *ua.OpenSecureChannelRequest with 132 bytes
debug: conn.go:388 uacp 3: recv OPNF with 135 bytes
debug: secure_channel.go:350 uasc 3/1: recv OPNF with 135 bytes
debug: secure_channel.go:289 uasc 3/1: recv *ua.OpenSecureChannelResponse
debug: secure_channel.go:304 uasc 3/1: sending *ua.OpenSecureChannelResponse to handler
debug: secure_channel.go:660 OpenSecureChannelResponse handler
debug: secure_channel.go:670 sc.handleOpenSecureChannelResponse
debug: secure_channel.go:698 uasc 3: received security token. channelID=29377 tokenID=1 createdAt=2025-09-17T14:09:35Z lifetime=1h0m0s
debug: client: monitor: secure channel recreated
debug: client: monitor: action: restoreSession
debug: client: monitor: trying to restore session
debug: secure_channel_instance.go:89 got sequence number 2
debug: secure_channel.go:868 uasc 3: security token expires at 2025-09-17T15:24:35Z. channelID=29377 tokenID=1
debug: secure_channel.go:1050 uasc 3/2: send *ua.ActivateSessionRequest with 141 bytes
===> 2025/09/17 16:09:35 state changed: Reconnecting
...
sub 1115503: recreate: subscription successfully recreated
debug: client: monitor: resuming 1 subscriptions
debug: client: monitor: resumed 1 subscriptions
debug: sub: pause: resume
debug: publish: pendingAcks=[{"SubscriptionID":1115502,"SequenceNumber":3}]
debug: publish: PublishRequest: {"RequestHeader":null,"SubscriptionAcknowledgements":[{"SubscriptionID":1115502,"SequenceNumber":3}]}
debug: secure_channel_instance.go:89 got sequence number 10
===> 2025/09/17 16:09:37 state changed: Connected
debug: secure_channel.go:1050 uasc 4/10: send *ua.PublishRequest with 106 bytes
Specifications
- Go Version:
go1.25.1 - OPCUA Version:
v0.8.0 - Platform:
linux/amd64