Close TCP connection after RO call to the same executable

I notice via netstat that the RO connections are kept alive when I do not want this
I have a set of HTTPS RO cloud servers that communicate with eachother.

My code is using a using() statement and disposes channel and message.
However the TCP connection remains established after the dispose.
The channel KeepAlive property is true

For the connections to other servers one TCP connection is created and reused.
For the connection to itself (via URL) the connection gets created multiple times.
After a period of time the TCP connection goes to TIME_WAIT and gets removed but multiple connections to oneself exist

What could be the reason? How do I explicitely dispose all resources?

Example of netstat connections a connection to oneself is shown on two lines (once client and once server perspective)
TCP 192.168.168.27:7572 192.168.167.10:50657 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:41160 TIME_WAIT
TCP 192.168.168.27:7572 192.168.168.1:41250 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:41254 ESTABLISHED
TCP 192.168.168.27:41250 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:41254 81.82.213.199:7572 ESTABLISHED
TCP [::]:7572 [::]:0 LISTENING

Hi,

can you give an example how are you call service methods, pls?

for example, you can use it as

var service := CoNewService(url);
...
service.NewMethod;
...
service.NewMethod1;

or

var service := CoNewService(url);
service.NewMethod;
...
var service1 := CoNewService(url);
service1.NewMethod1;

in 1st case one connection will be used, in 2nd one - two connections will be created

In our code we create 1 channel and 1 message (which we dispose) but then create multiple service interfaces (which we don’t dispose).
Is the service disposable? Should it be disposed?

This is our code

using (var cloudServerClient = new OffCloudServerClient.CloudServerClient(endpoint.url, userid, clientname, cloudpwd, tags,conconfig))
{
  if (cloudServerClient.connect(out errormsg))
  {
    cloudServerClient.itfManagement().getCloudServerUniqueID();
    ...
    cloudServerClient.itfManagement().getCloudData(...);  
  }
} // end using

The OffCloudServerClient class is our own helper class which supports IDisposable and has following relevant functions

bool connect(...)
{
 // creates another of our own custom helper classes which is put in member field roCon which contains code
        this.clientChannel = RemObjects.SDK.ClientChannel.ChannelMatchingTargetUri(uri);
        this.message = new RemObjects.SDK.BinMessage(); 
  // perform login
   bLoginOK = this.performLogin(false);
  // which contains
      var itfLogin = OffCloudServer.CoCloudPharmacyLoginService.Create(roCon.message, roCon.clientChannel);
      ...
      bool bRet=itfLogin.Login(curSessionUser, jsonLoginData);
}
    public void Dispose()
    {
      if (this.message != null)
        this.message.Dispose();
      if (this.clientChannel is IDisposable)
        ((IDisposable)this.clientChannel).Dispose();
    }
public OffCloudServer.IManagementService itfManagement()
{
   return OffCloudServer.CoManagementService.Create(roCon.message, roCon.clientChannel);
}

Hi,

no, it isn’t disposable.


try to change your code like

using (var cloudServerClient = new OffCloudServerClient.CloudServerClient(endpoint.url, userid, clientname, cloudpwd, tags,conconfig))
{
  if (cloudServerClient.connect(out errormsg))
  {
    var svc = cloudServerClient.itfManagement(); // reuse existing service
    svc.getCloudServerUniqueID();
    ...
    svc.getCloudData(...);  
  }
} // end using

and retest

OK, I will modify my code in a similar way.
FYI: I believe putting the keepalive parameter of the IpHttpClientChannel object to False already improved the situation

I still have some questions

  1. What about the login services? CoCloudPharmacyLoginService.Create. Each service call is preceded by such a service creation and call to Login.
  2. Should a RO service be disposed to free the TCP connection ASAP?
  3. In general: how can we release the TCP connection cleanly. I am afraid what happens now is not a perfect TCP finalization handshake. netstat shows FIN_WAIT_2 and TIME_WAIT for a few seconds.
    I could be mistaken but I believe a correct TCP finalization handshake instantly closes the connection.

TCP 192.168.168.27:7572 192.168.167.10:53780 FIN_WAIT_2
TCP 192.168.168.27:7572 192.168.167.10:53824 FIN_WAIT_2
TCP 192.168.168.27:7572 192.168.168.1:56670 TIME_WAIT
TCP 192.168.168.27:56671 81.82.213.199:7572 TIME_WAITl

Hi,

You can put login method to OnLoginNeeded event so it will be login when server requires it (i.e. when SessionNotFoundException is raised on server-side).

note: you are using HTTPS connections, so it has own logic. I’ve seen something similar on Delphi platform with SSL connections.

Can you reproduce the same case with plain http (not https) connections?

Yes, I have put the login method in the ClientChannel_OnLoginNeeded event. I am just pointing out that that function creates a login service each time.
Is that problematic?

Hi,

no, this event is raised when login is needed so it is as expected.

Can you tell me how a connection can be closed immediately?
The disposing of the channel does not appear to do the trick.

For a set of sequential calls create/connect/RO call/dispose a bunch of connections remain
It is not a big issue since they get cleaned up after 2 to 5 minutes but it makes analysing the network and port usage harder.

TCP 192.168.168.27:59903 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60317 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60334 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60335 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60336 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60337 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60338 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60345 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60346 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60347 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60348 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60350 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60351 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60355 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:60356 81.82.213.199:7572 ESTABLISHED

Hi,

Is it reproduced for HTTPS only or for HTTP too?


from 4.7. The Mysteries of Connection Close - HTTP: The Definitive Guide [Book]

Connection management—particularly knowing when and how to close connections—is one of the practical black arts of HTTP

Hi

I modified some code so it also supports plain HTTP.
The behaviour looks similar.
Note: Some simultaneous requests are performed so it is hard to evaluate how many connections are waranted but the dispose definately does not close the connection immediately

λ netstat -a -n | grep 7572
STDIN
TCP 0.0.0.0:7572 0.0.0.0:0 LISTENING
TCP 192.168.168.27:7572 78.21.37.126:54717 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55669 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55670 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55671 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55672 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55673 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55674 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55676 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55687 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55688 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55690 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55691 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55692 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55695 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55697 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55701 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55702 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55703 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55704 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55705 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55706 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55708 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55714 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55715 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55716 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55717 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55718 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55720 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55724 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55725 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55726 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55727 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55728 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55729 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55731 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55734 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55735 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55736 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55738 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55739 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55740 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55741 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55744 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55745 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55746 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55747 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55748 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55750 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55754 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55755 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55756 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55757 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55758 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55759 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55762 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55765 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55766 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55767 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55769 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55770 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55771 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55772 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55773 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55774 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55775 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55776 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55777 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55778 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55779 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55780 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55782 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55783 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55795 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55796 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55798 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55799 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55801 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55802 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55803 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55804 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55806 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55807 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55808 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55809 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55811 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55812 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55813 ESTABLISHED
TCP 192.168.168.27:7572 78.21.37.126:55814 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:62806 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:62825 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63008 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63009 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63010 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63011 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63032 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63033 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63034 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63035 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63039 ESTABLISHED
TCP 192.168.168.27:7572 192.168.168.1:63040 ESTABLISHED
TCP 192.168.168.27:62806 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:62825 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63008 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63009 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63010 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63011 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63032 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63033 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63034 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63035 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63039 81.82.213.199:7572 ESTABLISHED
TCP 192.168.168.27:63040 81.82.213.199:7572 ESTABLISHED
TCP [::]:7572 [::]:0 LISTENING

Hi,

as I see, we do socket.Shutdown and socket.Close at disposing channel. Check Connection class.

Looks like System.Net.Sockets.Socket has some delays …


from windows - Socket.Close doesn't really close tcp socket? (c#) - Stack Overflow

It will close the .NET part of the socket. However according to the TCP specification the OS have to keep the lower level tidbits of the socket open for a certain amount of time in order to detect retransmission, and similar. In this particular case it’s likely keeping the socket around for a bit in order to detect a reply to the SYN packet sent so it can reply more sensibly and not mix up the reply with further packets sent.