1

I have a weird case of (TCP) listening port conflicts on my hands.

The application uses a lot of ports. Less than a hundred but some tens. This is probably irrelevant, I get the conflict on the first bind operation which happens to be a listener.

I can repeatedly close and restart the application in quick succession without issue. To my awareness I neatly stop all threads and dispose all sockets on close.

The issue arises when the application is updated. To allow the executable and its dependencies to be overwritten, the update is delegated to an updater application. The main application starts the updater and immediately closes itself (in a graceful fashion, using a WM_CLOSE message). The updater unzips the update package and overwrites the binaries and what more. When done, it restarts the (now updated) main application.

At this point the main application reports the port conflict. It is a port that was used by the previous version.

I understand Windows reuses sockets under the hood, keeping them open even when an application closes them and then uses the same cached socket when the application connects again. So I figured Windows could be fooled by the new version, not recognizing it as the same application.

But here's the kicker. The updater stays up for a while, allowing the user to read the update report. The user can close it, if he doesn't it will automatically close after one minute. It appears that while the updater is running, the main application cannot be started without the port conflict occurring. As soon as the updater is closed, the main application can be started without issue again. And the updater itself does NOTHING with sockets!

Starting the updater and the main application is done using Process.Start(). It is as if something links the processes (of main app and updater). Task manager however confirms that the main application is really gone after is closed automatically.

Mind blown. Any insights would be much appreciated.

Martin Maat
  • 714
  • 4
  • 23
  • This sounds to me like your main application is not cleaning up resources properly, but the sockets are released by Windows when the process quits. `Process.Start()` may be starting the updater as a child process of the main application, so even though the main application has been closed, Windows doesn't clean up the resources while the updater is open. Could you show us the code for how you are creating and releasing your listener socket? – Andrew Williamson Dec 13 '22 at 18:30
  • Does this answer your question? [Prevent child process from inheriting parent process's opened TCP ports with boost process library](https://stackoverflow.com/questions/52357635/prevent-child-process-from-inheriting-parent-processs-opened-tcp-ports-with-boo) – NineBerry Dec 13 '22 at 19:51
  • 1
    Also interesting read: https://social.technet.microsoft.com/Forums/en-US/94ba760c-7080-4614-8a56-15582c48f900/child-process-keeps-parents-socket-open-diagnosticsprocess-and-nettcplistener?forum=netfxbcl – NineBerry Dec 13 '22 at 19:53
  • 1
    Very close: https://stackoverflow.com/questions/24676073/process-start-without-creating-a-child-process-port-handle-inheritance – NineBerry Dec 13 '22 at 19:54
  • 1
    @NineBerry Thank you very much! This looks promising, will try tomorrow. As I understand now, `Process.Start()` makes any spawned process inherit all Win32 handles. The lower level Win32 CreateProcess does provide an option to not inherit but `Process.Start()` options do not support it so you get the default behavior. It looks like I will have to use CreateProcess directly. – Martin Maat Dec 13 '22 at 21:48

1 Answers1

0

NineBerry's links were insightful but when trying to create an extension method for Process that takes an inherit argument I ran into the problem that ProcessStartInfo properties do not map nicely to the Win32 STARTUPINFO struct at all. This prevented me from keeping it compatible with existing code which used some features of ProcessStartInfo that I could not transfer to a call to CreateProcess. I do not understand how Process.Start() does this under the hood and could not be bothered anymore after I discovered a workaround.

It appears that setting ProcessStartInfo.UseShellExecute to true makes the whole problem go away. This may not be good for everybody because it has some additional properties but for me this was sufficient.

On GitHub people have asked for a ProcessStartInfo property that allows control over the the inherit value. It does not seem to be picked up yet and would likely only be implemented for future .NET Core releases.

A take on the seemingly discrepancy between ProcessStartInfo on the one hand and STARTUPINFO on the other hand would still be interesting so if anyone would care to explain, please do.

Martin Maat
  • 714
  • 4
  • 23