Child process keeps parent's socket open - Diagnostics.Process and Net.TcpListener
-
Tuesday, January 20, 2009 7:34 AM
Hello, everyone.
The short question is:
Does TcpListener create an inheritable socket, and does Process.Start allow child process to inherit it?
And here goes the background:
We've got an application, which spawns a child process, and interacts with it via TCP/IP. Aside from that, the application listens on one port continuously, and on another port periodically. If the application crashes, the orphaned child process continues to hold on to listening sockets until killed. This may not appear a problem - the child just times out and dies. The problem arises when the application, while still running, stops listening on the second port - the port isn't freed.
Can this be caused by child inheriting a handle?
Other ideas? :-(
Thanks,
Tony.
Answers
-
Monday, February 02, 2009 9:44 PM
OK; I did some more research, and I have come to the conclusion that the BCL causes all handles to be inherited by child processes by default. I have no idea why this decision was made, but it's apparently "by design".
So, you have two options available to you, and you'll have to P/Invoke either way (these are the same that bitterman0 specified, I'm just giving a bit more detail):
1) P/Invoke CreateProcess. This takes more work, but you'll be able to start a child process that doesn't inherit. The downside is that you won't be able to use redirected I/O. If you can live without redirected I/O, this is the cleaner solution (i.e., it won't inherit any of your handles).
2) P/Invoke SetHandleInformation. This is an easier solution, but you'll only mark certain handles as "don't inherit". The downside here is that all the other handles (files, etc) will still be inherited when Process.Start is called.
-Steve- Marked As Answer by Anton Morozov Tuesday, February 03, 2009 7:27 AM
All Replies
-
Wednesday, January 21, 2009 8:33 PMI seriously doubt that socket handles are inheritable by default in .NET. Of course, you could use Process Explorer to verify this.
What do you mean that the child holds on to listening sockets? From your brief description, I would expect the child process would act as a client...
Could you post a bit more detail on which sockets are created when, and by which process? In the meantime, check out Process Explorer (from Microsoft TechNet), which allows you to see sockets as well as all other handles in a process.
-Steve -
Monday, January 26, 2009 8:58 AM
I doubt that too, but the program doesn't seem to care... :-)
Process A can listen on ports 1, 2, and 3.
Port 1 is open always, it indicates that process A us up.
Port 2 can be opened and closed at will.
Port 3 is opened when process B is launched.Process B is spawned by process A, and immediately connects to its port 3. Yes, a client.
When process B terminates, process A closes port 3. Everything okay here.So the sequence is as follows:
- process A opens port 1
- process A spawns process B and opens port 3
- process B connects to port 3
- process A opens port 2 (no one connects to it)
- process A closes port 2, but it remains open, until process B terminates.Process B terminates at some point invisible to the user, and may not terminate at all before process A.
Process Explorer doesn't show handles as belonging to process B. To PE, handles disappear. But netstat still shows ports as open, and relates them to the now-nonexistent PID A. -
Monday, January 26, 2009 5:16 PM
What state are the ports in after process A exits?
-Steve
P.S. The makers of PE also wrote TcpView, which is sort of a GUI version of netstat. You may find it useful.
-
Tuesday, January 27, 2009 4:31 AM
Listening.
-
Tuesday, January 27, 2009 12:15 PMModeratorThis doesn't make a lot of sense of course. First thing to do is to disable shrink-wrapped malware on your machine. Virus scanners are way too interested in TCP/IP connections. Work your way from there to the firewall.
Hans Passant. -
Tuesday, January 27, 2009 9:12 PM
Which ports?
This isn't making much sense; part of Windows process teardown is to force closed all the handles they have...
-Steve
-
Wednesday, January 28, 2009 6:46 AM
Listening ports are 19328-19330, client ports are auto-assigned.
I can't deactivate the firewall/antivirus - have no rights, and absolutely no ways to reach the admin.
Of course, problem does not show up on a sample project...Thanks for TcpView, Stephen. Nice program, pity it can't filter out UDP or particular interfaces... Oh, it shows several instances of listening ports! Duplicate handles?
I've managed to crash process A at the moment when it had lots of CLOSE_WAIT sockets. They, too, were kept by process B, and weren't closed after whatever is their timeout - were hanging there for quite some time.Tony.
P.S.: Oh, I got it - the duplicate "listening" ports are established connections 8-(
I've checked Dispose routines (System.Net classes are wrapped in our own ones) and inspected memory snapshots - no sign of unclaimed Sockets.Sockets or anything... -
Thursday, January 29, 2009 5:40 PM
For multiple listening ports, check that the local addresses are the same. If they're not, then it's really different ports. That said, newer Microsoft OSes allow multiple programs to listen on the same port unless they insist on listening exclusively. Note that established connections show up as "established" in TCPView, not "listening".
CLOSE_WAIT is perfectly normal, even after a process exits. It doesn't mean the other process owns them. The default timeout is 240 seconds, but may be dynamically adjusted.
At this point, I think it's best to take a step back. Are you getting any exception messages when this problem occurs?
-Steve
-
Monday, February 02, 2009 7:23 AMNo, listening ports are all different, no overlaps.
Yes, I see ESTABLISHED, but sometimes TcpView indeed shows LISTENING for server-side established sockets.
Okay, maybe CLOSE_WAITs were okay in terms of a timeout, but they've disppeared instantly when process B was killed.
No, no exceptions. TcpListener.Close() executes okay, but nothing happens to the socket. TcpListener.Start() crashes later, of course.
Recently another bug was brought up. Process A can work with process M (kind of a "manager"), and they can launch each other.
- process A opens port 1,
- process A launches process M,
- process A terminates,
- process M launches process A again,
- process A crashes while opening port 1, because it is held by process M. -
Monday, February 02, 2009 7:29 PMThe truth about win32 CreateProcess API (and Process.Start() by association) is that it is capable of letting the child process to inherit some open handles from the parent process. See bInheritHandles parameter for CreateProcess() function. Ordinarily, when you create handles in win32 processes (e.g. open files, sockets, pipes, what-have-you), you specify whether the handle should be inheritable to begin with; and furthermore, when you spawn a new child, you specify whether it is going to inherit any of your handles (the ones you marked as inheritable earlier).
Handle inheritance is the mechanism used in input/output redirection, if there are any doubts that .NET has anything to do with it.
I suggest you start digging in this direction. Two options are available to you -- either make your socket handles not inheritable or use direct win32 API calls to spawn new processes. -
Monday, February 02, 2009 9:44 PM
OK; I did some more research, and I have come to the conclusion that the BCL causes all handles to be inherited by child processes by default. I have no idea why this decision was made, but it's apparently "by design".
So, you have two options available to you, and you'll have to P/Invoke either way (these are the same that bitterman0 specified, I'm just giving a bit more detail):
1) P/Invoke CreateProcess. This takes more work, but you'll be able to start a child process that doesn't inherit. The downside is that you won't be able to use redirected I/O. If you can live without redirected I/O, this is the cleaner solution (i.e., it won't inherit any of your handles).
2) P/Invoke SetHandleInformation. This is an easier solution, but you'll only mark certain handles as "don't inherit". The downside here is that all the other handles (files, etc) will still be inherited when Process.Start is called.
-Steve- Marked As Answer by Anton Morozov Tuesday, February 03, 2009 7:27 AM
-
Tuesday, February 03, 2009 7:29 AMThanks, Stephen!
What I didn't find myself was that all handles are inheritable. Thanks, again.
Pheew, this bug took so long :-) -
Tuesday, February 03, 2009 11:27 AMActually, there is a third possible solution (the "recommended" one):
3) Start all processes before you open the "interesting" handles.
In many cases, though, this just isn't possible.
-Steve -
Friday, September 16, 2011 10:06 AM
Thanks, I just ran into this problem and it was driving me crazy. Doing a pinvoke to CreateProcess so that I could disallow inheriting handles worked great.
--Michael

