Forcefully closing an SCU TCP connect attempt

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
jogerh
Posts: 37
Joined: Mon, 2022-02-28, 08:55

Forcefully closing an SCU TCP connect attempt

#1 Post by jogerh »

Ultrasound scanners typically send DICOM images to a server using a spooler mechanism that allow images to be sent in the background while the system is being used for image scanning. While the system is sending images, it may be necessary to quickly shut down the system. At the time the system is being shut down, the network may already have been disconnected. If the Storage SCU is attempting to establishing an association at this time, the shutdown will be delayed by the remaining duration of the TCP connect timeout for the Storage SCU connection.

Reducing the connect timeout is not really a viable option, because some servers may require a long TCP connect timeout to respond. Terminating the thread responsible for establishing the association is also not a viable option, because it may lead to deadlock during shutdown.

I have two questions related to solving this shutdown delay:
1. One idea could be to chop the TCP connect timeout up in shorter timeouts, and repeat short timeout TCP connect attempts for a total duration of the desired timeout. Does anyone have experience with this technique? With TCP, would repeated connect attempts serve the same purpose as one long timeout connect attempt, or are these two ways of connecting fundamentally different?
2. With DCMTK, is there a way to forcefully end a negotiation attempt that is currently blocking in the TCP connect stage?

J. Riesmeier
DCMTK Developer
Posts: 2501
Joined: Tue, 2011-05-03, 14:38
Location: Oldenburg, Germany
Contact:

Re: Forcefully closing an SCU TCP connect attempt

#2 Post by J. Riesmeier »

Re. 2: I am not aware this. I have looked at the underlying requestAssociationTCP() function in dcmnet/libsrc/dulfsm.cc. There, you can also see how the "connection timeout" is implemented in the DCMTK. Maybe, you'll find this helpful...

Re. 1: You could also start with a rather short connection timeout, which is still sufficient for most cases, e.g. 30 seconds, and if this elapsed without a connection being established, try again with a longer one, e.g. 60 seconds or 300 seconds. What do you think?

jogerh
Posts: 37
Joined: Mon, 2022-02-28, 08:55

Re: Forcefully closing an SCU TCP connect attempt

#3 Post by jogerh »

Thank you for the input!

https://github.com/DCMTK/dcmtk/pull/62 shows a preliminary sketch which could be one way to be able to cancel ongoing association attempts while the DcmSCU is attempting to establish a TCP connection to the unreachable server. It allows waking the dulfsm.cc requestAssociationTCP's blocking select() call before the timeout elapses using a socketpair (not shown yet, but would be implemented by the client code that uses DcmSCU).

Another option would be to introduce a 'dcmTcpPollingInterval' and a 'cancelConnection' pointer parameter to the DUL_ASSOCIATESERVICEPARAMETERS structure. We could then set the dcmTcpPollingInterval to for example 3-5 seconds. In dulfsm.cc requestAssociationTCP, we could then call 'select()' in a loop with the dcmTcpPollingInterval as timeout until we reach the desired connectionTimeout. This would allow us to jump out of the select loop if cancelConnection was set to true in the meantime. This could be a viable alternative if https://github.com/DCMTK/dcmtk/pull/62 is out of the question.

Please have a look, let me know if this is something that DCMTK would benefit from in some shape or form.

Horst Balthasar
Posts: 29
Joined: Mon, 2021-02-01, 11:32

Re: Forcefully closing an SCU TCP connect attempt

#4 Post by Horst Balthasar »

I have a similar problem. I use the class DcmScu to retrieve the worklist and send the captured images to the server.
When the network connection is very slow or even fails, it seems like the API functions don't return any result and block my thread.
I use the default settings regarding network timeout and ACSE timeout, but it seems that these timeouts do not work.
I assume this API should wait until timeout occurs based on the values set to network or ACSE timeout. In may case the default values.

I am using dcmtk 3.6.5 on linux.

I have the following question:

How do I understand these timeouts and do I need to set some flag to activate these timeouts?

Best regards,

Horst

Horst Balthasar
Posts: 29
Joined: Mon, 2021-02-01, 11:32

Re: Forcefully closing an SCU TCP connect attempt

#5 Post by Horst Balthasar »

I would like to explain the problem I am having in more detail.
I have added some log messages in my function (e.g. for sending the C-ECHO request), namely:

initialize network ...
negotiating network association ...
Releasing Association.

When the network is very slow or down, the message "Releasing Association" is missing in my log file and it seems that the API function sendEchoRequest is still pending and it would not finish.
As mentioned earlier, I am using the default settings for the timeouts. i.e. I am not calling any of the setConnectionTimeout, setACSETimeout, or setDIMSETimeout functions of the DcmSCU class.

According to a statement of Mr. Riesmeier the timeout for the network is set to 60s. I also get a corresponding message in my log files.

2023-03-03 10:22:37.553 DEBUG: setting network send timeout to 60 seconds
2023-03-03 10:22:37.553 DEBUG: setting network receive timeout to 60 seconds

Which of the timeouts are displayed here ?

I found the following code in the function requestAssociationTCP in the file dulfsm.cc:

// get global connection timeout
Sint32 connectTimeout = dcmConnectionTimeout.get();

if (connectTimeout >= 0)
{
// user has specified a timeout, switch socket to non-blocking mode
#ifdef HAVE_WINSOCK_H
ioctlsocket(s, FIONBIO, (u_long FAR *) &arg);
#else
flags = fcntl(s, F_GETFL, 0);
fcntl(s, F_SETFL, O_NONBLOCK | flags);
#endif
}

The question I am asking now is, if I set dcmConnectionTimeout to a value > 0, that then this timeout is active ?


Best regards,

Horst

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Forcefully closing an SCU TCP connect attempt

#6 Post by Michael Onken »

Hi Horst,

this requires some more investigation from my/our side. I will have a look at this week.

Best regards,
Michael

Horst Balthasar
Posts: 29
Joined: Mon, 2021-02-01, 11:32

Re: Forcefully closing an SCU TCP connect attempt

#7 Post by Horst Balthasar »

Hi, Michael,

Meanwhile, do you have any new findings regarding the timeouts in your api ?

Best regards,
Horst

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Forcefully closing an SCU TCP connect attempt

#8 Post by Michael Onken »

Not yet, sorry

J. Riesmeier
DCMTK Developer
Posts: 2501
Joined: Tue, 2011-05-03, 14:38
Location: Oldenburg, Germany
Contact:

Re: Forcefully closing an SCU TCP connect attempt

#9 Post by J. Riesmeier »

According to a statement of Mr. Riesmeier the timeout for the network is set to 60s. I also get a corresponding message in my log files.

2023-03-03 10:22:37.553 DEBUG: setting network send timeout to 60 seconds
2023-03-03 10:22:37.553 DEBUG: setting network receive timeout to 60 seconds

Which of the timeouts are displayed here ?
As the log messages indicate, the above listed timeouts refer to the "network send" and "network receive" functions:

Code: Select all

/** Global timeout in seconds for sending data on a socket to a remote host.
 *  The default value is 60, which is useful in cases where the sender (e.g.
 *  storescu) looses the connection to the receiver because the network cable
 *  is pulled (e.g. for a mobile device).
 *  A value of 0 means that the send() will never timeout, and a value of -1
 *  disables the call of the corresponding setsockopt() function, so that the
 *  system's default behavior remains unchanged.
 */
extern DCMTK_DCMNET_EXPORT OFGlobal<Sint32> dcmSocketSendTimeout;   /* default: 60 */

Code: Select all

/** Global timeout in seconds for receiving data on a socket from a remote host.
 *  The default value is 60, which is useful in cases where the receiver (e.g.
 *  storescp) looses the connection to the sender because the network cable is
 *  pulled (e.g. for a mobile device).
 *  A value of 0 means that the recv() will never timeout, and a value of -1
 *  disables the call of the corresponding setsockopt() function, so that the
 *  system's default behavior remains unchanged.
 */
extern DCMTK_DCMNET_EXPORT OFGlobal<Sint32> dcmSocketReceiveTimeout;   /* default: 60 */

Horst Balthasar
Posts: 29
Joined: Mon, 2021-02-01, 11:32

Re: Forcefully closing an SCU TCP connect attempt

#10 Post by Horst Balthasar »

Thanks for our answers.

Best regards,

Horst

Horst Balthasar
Posts: 29
Joined: Mon, 2021-02-01, 11:32

Re: Forcefully closing an SCU TCP connect attempt

#11 Post by Horst Balthasar »

Unfortunately I have to come back to the different timeouts in your DCMTK, because for me some things are still unclear and incomprehensible.
In my opinion, if a network connection does not receive a response within a certain time, a so-called timeout error should occur.

Since there are several timeouts in DCMTK, which timeout occurs, if the server does not answer? What is the effect of the blocking mode?

In the case of the default settings, I could see that the request to the DICOM server remains pending and does not terminate. I could also determine this with echoscu.

What can I do to get back a timeout error if I cannot connect to the server, be the cable is not plugged in or the connection is very slow? And if this error occurs, can I repeat the request?

Best regards
Horst

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Forcefully closing an SCU TCP connect attempt

#12 Post by Michael Onken »

Hi Horst,

regarding your questions:
In the case of the default settings, I could see that the request to the DICOM server remains pending and does not terminate. I could also determine this with echoscu.
as stated by echoscu help page, the default is "unlimited", which seems to be in line with what you see on your system. I also tried connection timeout of 30 seconds on Windows and Linux:

Code: Select all

PS C:\Users\onken> Measure-Command {echoscu dicomserver.co.uk 105 -to 30}
F: Association Request Failed: 0006:031c TCP Initialization Error: Der Vorgang wurde erfolgreich beendet. (Timeout)

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 30
Milliseconds      : 87
Ticks             : 300875312
TotalDays         : 0,000348235314814815
TotalHours        : 0,00835764755555556
TotalMinutes      : 0,501458853333333
TotalSeconds      : 30,0875312
TotalMilliseconds : 30087,5312

Code: Select all

❯ time echoscu dicomserver.co.uk 105 -to 30
F: Association Request Failed: 0006:031c TCP Initialization Error: Operation now in progress (Timeout)

real	0m30,062s
user	0m0,029s
sys	0m0,004s
which also meets my expectations (at least;).

Code: Select all

which timeout occurs, if the server does not answer?
  • Connection Timeout: If a connection timeout is specified, the client stops an ongoing, unsuccessful TCP connection request after the given number of seconds, if it is not possible to start the TCP connection with the server within this time. Otherwise, the client will try to connect forever. I think there can also be a system interrupt (out of control for the developer) that stops the connection attempt but I am not sure when this can happen. echoscu's default is unlimited, which can be changed using the --timeout option. If you use DcmSCU, you can specify a custom timeout by calling DcmSCU::setConnectionTimeout(t) where t is either -1 (wait forever), 0 (return immediately if connection does not work) or number of seconds; default is taken from a global variable dcmConnectionTimeout, which is per default -1 (i.e. wait forever).
  • ACSE timeout: Once the TCP connection is established, there is an ACSE and a DIMSE timeout. The ACSE timeout specifies for example, how long you want to wait for a response from the server for an ACSE request message that you have sent until you give up. The timeout is (I think) also used in other occassions during association negotiation, always dealing with how long to wait for the remote association partner to send further expected data packages). The default for the ACSE timeout inside the dcmnet library is 100 seconds. In echoscu, you can set this value using the --acse-timeout option. If you use DcmSCU as a client, it sets the timeout interally to 30 seconds instead, or to the user-defined value provided using DcmSCU::setACSETimeout().
  • DIMSE timeout: The DIMSE timeout does the same for DIMSE message (e.g. how long you wait for a C-STORE-RSP after issuing a C-STORE-RQ). The default, set in most (all?) tools is "unlimited", i.e. the library will wait forever. You can override this in echoscu using the --dimse-timeout option, and in DcmSCU by using DcmSCU::setDIMSETimeout().
  • DIMSE blocking mode Very often, it is not desirable to wait forever for a connection or response since it blocks the requesting code and there is "nothing" you can do in order to get control back. That's why, this "waiting forever" is called "blocking mode". In DCMTK, If a connection or ACSE timeout is set, it is automatically "non-blocking", i.e. the code will return control if there is no connection / data and the user can decide what to do next. However, for DIMSE, one has not only to set the timeout but to also to explicitly choose "non-blocking" mode. Tools like echoscu do this automatically if you set the timeout (--dimse-timeout option). In DcmSCU, you must set the timeout and also use DcmSCU::setDIMSEBlockingMode(...) to enable non-blocking mode, otherwise the timeout will be ignored.
  • Socket Timeout: This is kind of a "catchall" timeout, i.e. independent from the connection and DICOM-specific timeouts, since it applies to the TCP connection in general. There are two global settings (i.e. not configurable per connection), dcmSocketSendTimeout and dcmSocketReceiveTimeout which default to 60 seconds. If no data is received or sent within the specified timeout, the underlying TCP data sending/receiving attempt it cancelled and control is returned to the user. In echoscu you can set this timeout using --socket-timeout. In DcmSCU, you cannot set this, but instead, DcmSCU makes use of the global variables dcmSocketSendTimeout and dcmSocketReceiveTimeout, which you can set to the value you prefer.
Please note that this is all compiled to the best of my knowledge. We would have to carefully document this again centrally and probably also test it completely again. I hope this helps.

BR,
Michael

Horst Balthasar
Posts: 29
Joined: Mon, 2021-02-01, 11:32

Re: Forcefully closing an SCU TCP connect attempt

#13 Post by Horst Balthasar »

Thanks for our answers.

Best regards,
Horst

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 1 guest