System.Net.Sockets.Socket question

  • Follow


I have an application that accepts an existing socket connection
(passed to it by the server that actually accepted the incoming
connection), and I'm trying to find a way to have the application NOT
close the socket when it terminates.

So basically what I've done is created a class that inherits from
System.Net.Sockets.Socket.  In the .NET source code I can see that the
Dispose() method closes the socket if it is still open, so my first
thought was to override the Dispose() method and leave out the socket
closing portion of the code.  This failed, so maybe my understanding
of overriding isn't correct, and the base method still gets called?

Anyway, in my trial and error attempts I accidentally stumbled across
a method that works, and seems reliable on my machine, but I'd like
something I can be a little more certain of on other machines.
Basically if I call System.Threading.Thread.Sleep(2500) in my
overridden Dispose(), the connection stays open after the application
terminates.

Like I said, I have no idea if this will hold true on other machines,
which is why I'm wondering if there is an easier/better/more reliable
way to keep a Socket from being closed when an application quits?
0
Reply Rick 1/21/2010 6:00:55 PM

Rick Parrish wrote:
> [...]
> Like I said, I have no idea if this will hold true on other machines,
> which is why I'm wondering if there is an easier/better/more reliable
> way to keep a Socket from being closed when an application quits?

IMHO, the first step is to understand better why you think it's a good 
idea to leave the socket open.  Even if you successfully terminate your 
application without closing the socket, the OS is going to notice and 
eventually close it on your behalf.

Without seeing the code, it's impossible to know for sure why one 
approach you tried had the appearance of the effect you wanted while 
another did not.  It's possible that your call to Sleep() delayed the 
finalizer thread enough that the run-time just gave up on it, thus 
interrupting the disposal (but also the finalization of EVERY OTHER 
OBJECT also needing finalization!).

But really, I think it would be more useful to try to discuss whatever 
actual problem you're trying to solve, than this particular solution to 
the problem you've decided upon.  The solution you're trying to 
implement seems like a bad idea in any case, and once your process has 
terminated, it's unlikely to have any specific lasting effect (i.e. 
probably won't do what you seem to want it to do anyway).

Pete
0
Reply Peter 1/21/2010 6:29:56 PM


> IMHO, the first step is to understand better why you think it's a good
> idea to leave the socket open. =A0Even if you successfully terminate your
> application without closing the socket, the OS is going to notice and
> eventually close it on your behalf.

Any experience with old BBS Software?  Basically I'm looking to make a
modern equivalent.  So I have a telnet server accepting telnet
connections, and after connecting, a user may want to run an external
program.  So the server launches the program and passes the socket
handle so the program can communicate with the user.  When the program
quits, the user should go back to the telnet server so they can do
something else, but if the program closes the socket, then the user is
just disconnected.

I implemented this long ago in Delphi, so I know the OS will allow
what I want, now it's just a matter of whether the Socket class can do
what I want, or if I'll have to rewrite it for this one stupid little
feature.

> Without seeing the code, it's impossible to know for sure why one
> approach you tried had the appearance of the effect you wanted while
> another did not. =A0It's possible that your call to Sleep() delayed the
> finalizer thread enough that the run-time just gave up on it, thus
> interrupting the disposal (but also the finalization of EVERY OTHER
> OBJECT also needing finalization!).

This is what I was thinking, and also why I'm looking for a better
solution
0
Reply Rick 1/21/2010 6:59:44 PM

Hi,

> I'm trying to find a way to have the application NOT
> close the socket when it terminates.

It would be similar to leaving a file open even if the app is terminated. I 
would not be surprised if it was just not possible as AFAIK all resources 
owned by a process are supposed to be cleaned up when the process dies...

You may want to explain fist what is your overall goal...

--
Patrice
 


0
Reply Patrice 1/21/2010 7:11:24 PM

Rick Parrish wrote:
>> IMHO, the first step is to understand better why you think it's a good
>> idea to leave the socket open.  Even if you successfully terminate your
>> application without closing the socket, the OS is going to notice and
>> eventually close it on your behalf.
> 
> Any experience with old BBS Software?  Basically I'm looking to make a
> modern equivalent.  So I have a telnet server accepting telnet
> connections, and after connecting, a user may want to run an external
> program.  So the server launches the program and passes the socket
> handle so the program can communicate with the user.

That doesn't make sense.  Socket handles are valid only within the 
owning process.  You have to use WSADuplicateSocket(), directly or 
indirectly, to marshal the socket information to another process for it 
to use the socket.

Note that when using WSADuplicateSocket(), one process can close the 
socket without affecting the other.  They share the same socket 
instance, and the OS keeps track of how many processes are using the 
socket, only actually closing the socket when the last process is done 
with it.

(Obviously you can't use the .NET method Socket.DuplicateAndClose(), 
because it automatically closes the socket in the current process; but 
the unmanaged Winsock API supports leaving the duplicated socket open).

> [...]
> I implemented this long ago in Delphi, so I know the OS will allow
> what I want, now it's just a matter of whether the Socket class can do
> what I want, or if I'll have to rewrite it for this one stupid little
> feature.

Frankly, I'm still not even sure sharing a socket is the best approach. 
  A much more common approach would be to make the external process use 
stdin and stdout, and have the original parent process be the only one 
using the socket.  This avoids having to deal with any synchronization 
issues between processes sharing the socket, and provides a more 
general-purpose interface too.  The parent process can just redirect the 
input and output to the child process, and handle all the communications 
with the remote endpoint itself, proxying communications between the 
remote endpoint and the child process.

Pete
0
Reply Peter 1/21/2010 7:15:26 PM

> That doesn't make sense. =A0Socket handles are valid only within the
> owning process. =A0You have to use WSADuplicateSocket(), directly or
> indirectly, to marshal the socket information to another process for it
> to use the socket.

Using CreateProcess() with bInheritHandles=3Dtrue allows the child to
use the socket, and I guess that must be what Process.Start() does
when ProcessStartInfo.UseShellExecute=3Dfalse.  I'll have to try using
WSADuplicateSocket() later tonight.

> Note that when using WSADuplicateSocket(), one process can close the
> socket without affecting the other. =A0They share the same socket
> instance, and the OS keeps track of how many processes are using the
> socket, only actually closing the socket when the last process is done
> with it.

That sounds like exactly what I'd need then.

> Frankly, I'm still not even sure sharing a socket is the best approach.

I'd agree there are better ways, but there are several telnet servers
already available that may not support those other ways.  The only one
they all have in common is sharing a socket, so that's what I'm stuck
with.
0
Reply Rick 1/21/2010 7:33:15 PM

Rick Parrish wrote:
>> That doesn't make sense.  Socket handles are valid only within the
>> owning process.  You have to use WSADuplicateSocket(), directly or
>> indirectly, to marshal the socket information to another process for it
>> to use the socket.
> 
> Using CreateProcess() with bInheritHandles=true allows the child to
> use the socket, and I guess that must be what Process.Start() does
> when ProcessStartInfo.UseShellExecute=false.

Huh?  I don't have time to try it now, but�are you saying that _by 
default_, doing nothing special, not only is the underlying SOCKET 
handle in the Socket class inheritable, but the Process class is 
starting the process with bInheritHandles set to TRUE?

That sounds like a very serious security bug to me.  Want access to some 
inheritable handle in a process that starts other processes?  No 
problem�just replace a known target child process executable with your 
own, and voil�, carte blanche access.

Can you post a concise-but-complete code example demonstrating it?

> [...]
>> Frankly, I'm still not even sure sharing a socket is the best approach.
> 
> I'd agree there are better ways, but there are several telnet servers
> already available that may not support those other ways.  The only one
> they all have in common is sharing a socket, so that's what I'm stuck
> with.

I guess I don't really get why, just because other telnet servers 
implement it this way, you feel yours needs to.  The way I suggested is 
more general purpose (works with any random console application), while 
depending on handle inheritance requires the cooperation of the child 
process (you still need to pass the handle value to the child process, 
so it knows _which_ handle to use).

But, whatever�if you insist on doing this, I guess you can share handles 
if you like.  I also don't understand why an inherited handle wouldn't 
have the same behavior as a duplicated one; that is, it should have to 
be closed in all processes in which it's valid before the OS object is 
actually closed.  But since it's not a technique I'd use in this way, 
I'm not going to worry too much about that.  :)

Pete
0
Reply Peter 1/21/2010 11:31:53 PM

> Huh? =A0I don't have time to try it now, but=85are you saying that _by
> default_, doing nothing special, not only is the underlying SOCKET
> handle in the Socket class inheritable, but the Process class is
> starting the process with bInheritHandles set to TRUE?

Just had a look at Process.cs in the .NET source, and yes, if
UseShellExecute=3Dfalse then Process.Start() will instead use
CreateProcess(), and in that case, bInheritHandles is hardcoded to
true.

> I guess I don't really get why, just because other telnet servers
> implement it this way, you feel yours needs to. =A0The way I suggested is
> more general purpose (works with any random console application), while
> depending on handle inheritance requires the cooperation of the child
> process (you still need to pass the handle value to the child process,
> so it knows _which_ handle to use).

It's not that I necessarily want to do things the same way other
telnet servers do it, it's that I have to if I want to maintain
compatibility with them.  The 3rd party programs made with this code
could be used under one of many different telnet servers, and while
one does actually support communicating via standard input/output, the
rest only use shared sockets.  Passing the handle value to the child
process is standard amongst off the servers, so that's not a problem.

> But, whatever=85if you insist on doing this, I guess you can share handle=
s
> if you like. =A0I also don't understand why an inherited handle wouldn't
> have the same behavior as a duplicated one; that is, it should have to
> be closed in all processes in which it's valid before the OS object is
> actually closed. =A0But since it's not a technique I'd use in this way,
> I'm not going to worry too much about that. =A0:)

My guess is that the OS probably doesn't track when inherited handles
are used in multiple processes.  It's easy for it to know when
duplicated handles are used, since WSADuplicateSocket() and WSASocket
() have to be used, but with inherited handles I can just pass the ID
of the socket from the parent to the child, and then directly use send
(ID, ...) without doing anything extra.

In the end I've decided just to write my own stripped down Socket.cs
that won't close on dispose.  Bit more work than I was hoping for, but
it'll give me the most control.
0
Reply Rick 1/22/2010 1:48:06 AM

Just for future reference (not that I expect many people will be
interested), I found a much simpler solution that isn't as
questionable as Thread.Sleep(2500) or as much work as creating an
entirely new Socket.cs:

// FSocket is my System.Net.Socket
object m_Handle = typeof(Socket).GetField("m_Handle",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(FSocket);
Assembly SystemAssembly = Assembly.LoadFile(@"C:\WINDOWS\Microsoft.NET
\Framework\v2.0.50727\System.dll");
SystemAssembly.GetType("System.Net.SafeCloseSocket").GetField
("m_InnerSocket", BindingFlags.NonPublic |
BindingFlags.Instance).SetValue(m_Handle, null);

In Socket.cs, m_Handle is a private field of type SafeCloseSocket,
which in turn has a private field called m_InnerSocket.  The above 3
lines effectively set m_InnserSocket to null, so when the cleanup code
occurs, it bypasses the call to closesocket(), leaving our inherited
handle intact when the program terminates.
0
Reply Rick 1/25/2010 2:05:36 PM

Rick Parrish wrote:
> [...]
> In Socket.cs, m_Handle is a private field of type SafeCloseSocket,
> which in turn has a private field called m_InnerSocket.  The above 3
> lines effectively set m_InnserSocket to null, so when the cleanup code
> occurs, it bypasses the call to closesocket(), leaving our inherited
> handle intact when the program terminates.

Quite frankly, that is an _awful_ solution.  It is entirely 
implementation dependent, and likely to fail to work in some future 
version of .NET.  It definitely is "as questionable as" using 
Thread.Sleep().

It's bad enough you find yourself needing to share socket handles 
between processes.  Don't compound the problem by using reflection to 
get at implementation dependent particulars that could change at any time.

If you insist on relying on a handle-sharing implementation for your own 
code, at least do it in a reliable way, such as providing your own 
socket implementation that allows you to leave a socket unclosed.

Of course, even doing that you have failed to take into account the fact 
that the _unmanaged_ object is still effectively managed by the OS, and 
will still be closed at some point after your process exits.  But if for 
some reason not closing it explicitly when your process exits lets it 
live long enough for your purposes, then at least achieve that goal in a 
reliable way.

Pete
0
Reply Peter 1/25/2010 5:30:54 PM

> Quite frankly, that is an _awful_ solution. =A0It is entirely
> implementation dependent, and likely to fail to work in some future
> version of .NET. =A0It definitely is "as questionable as" using
> Thread.Sleep().

Excellent point.  This was the first time I'd tried using reflection,
and I was so happy it worked that the thought didn't even occur to me
that it'll likely break down the road.  So I've implemented my own
Socket class, and all is well now.

> Of course, even doing that you have failed to take into account the fact
> that the _unmanaged_ object is still effectively managed by the OS, and
> will still be closed at some point after your process exits. =A0But if fo=
r
> some reason not closing it explicitly when your process exits lets it
> live long enough for your purposes, then at least achieve that goal in a
> reliable way.

Maybe I'm wrong, but I would think the OS will let it live until the
parent process has exited as well (which won't happen until some time
after the child process does).
0
Reply Rick 1/27/2010 3:49:02 PM

Rick Parrish wrote:
> [...]
>> Of course, even doing that you have failed to take into account the fact
>> that the _unmanaged_ object is still effectively managed by the OS, and
>> will still be closed at some point after your process exits.  But if for
>> some reason not closing it explicitly when your process exits lets it
>> live long enough for your purposes, then at least achieve that goal in a
>> reliable way.
> 
> Maybe I'm wrong, but I would think the OS will let it live until the
> parent process has exited as well (which won't happen until some time
> after the child process does).

Time will tell.

I admit, I am so skeptical of the overall approach, I haven't really 
bothered to look into it in detail.  But, the only reason I can think of 
that the OS would let a handle in one process stay open after the 
process exits is if the OS is specifically tracking how many processes 
are using the handle, and on the basis of a count of such processes, 
leaves the handle open until the count is 0.

But, if the OS is doing that, I would expect the OS to also use that 
logic when any given process closes the handle.  This is in fact the 
documented behavior for inheritable handles generally.  So if you have a 
situation where the OS isn't doing what the docs appear to say it would 
normally do, I don't think you can count on the OS also doing the same 
kind of thing when the handle would be closed implicitly due to process 
exit.

However, that's all pure speculation.  If it seems to work for you and 
you're happy with the solution, go for it.  :)

Pete
0
Reply Peter 1/27/2010 6:06:48 PM

> I have an application that accepts an existing socket connection
> (passed to it by the server that actually accepted the incoming
> connection), and I'm trying to find a way to have the application NOT
> close the socket when it terminates.

Ironically enough, the way to stop the socket from closing is to call
Socket.Close()!

After much more testing and stepping through code I found
Socket.Dispose() calls shutdown(), and this is what was disconnecting
the user.  Calling Socket.Close() will call closesocket() without
calling shutdown(), which leaves the connection open, and so then when
the Socket is disposed, shutdown() doesn't get called since Close()
has already been called.
0
Reply Rick 1/28/2010 7:34:50 PM

12 Replies
290 Views

(page loaded in 0.401 seconds)

Similiar Articles:































7/20/2012 1:25:49 PM


Reply: