Curious Socket Behavior

S

Steve Ricketts

I'm using asynchronous TCP sockets in a C# client application which is
trying to match an old VB6 program behavior. When the server starts to
close (also written in VB6 using winsock) it sends an "EX" code to all the
connected clients telling them to close and then closes all the client
winsock connections. In VB, the client received the "EX" command and began
the shutdown process. However, in the C# program I immediately get 0 length
data from the server like it's closed the connection before sending the
"EX"... I never see the "EX". There is a very small amount of time between
sending the "EX" and closing the socket, but I'm curious what exactly is
happening. I would like to be able to distinguish between a normal shutdown
(receiving the "EX") and simply loosing the connection. Without the "EX" I
don't know whether to try and reconnect or if I was supposed to shut down.

Can anyone give me a brief explanation of what is happing?

Thanks,

Steve
 
A

Adam Clauss

Steve said:
I'm using asynchronous TCP sockets in a C# client application which is
trying to match an old VB6 program behavior. When the server starts
to close (also written in VB6 using winsock) it sends an "EX" code to
all the connected clients telling them to close and then closes all
the client winsock connections. In VB, the client received the "EX"
command and began the shutdown process. However, in the C# program I
immediately get 0 length data from the server like it's closed the
connection before sending the "EX"... I never see the "EX". There is
a very small amount of time between sending the "EX" and closing the
socket, but I'm curious what exactly is happening. I would like to be
able to distinguish between a normal shutdown (receiving the "EX") and
simply loosing the connection. Without the "EX" I don't know whether
to try and reconnect or if I was supposed to shut down.

Can anyone give me a brief explanation of what is happing?

Thanks,

Steve
How are you closing the socket on the server side?

I'm assuming you are effectively ending up with something similar to:

// Send EX
socket.BeginSend(...);
// Close socket
socket.Close();


Because the BeginSend is asynchronous (and the Close is not), the Close
may actually end up occurring prior to the send, thus you see the
receive of 0 length data on the client side. Prior to closing, try
calling socket.Shutdown(). That takes an enumeration value as a
parameter. I forget the name, but the value I believe should be
"both". That will force the socket to clear all receive/send buffers,
so you can then Close safely knowing that the EX got sent.

socket.BeginSend(...);
socket.Shutdown(EnumerationType.Both); // need to change EnumerationType
to the correct name)
socket.Close();

-Adam
 
A

Adam Clauss

Adam said:
How are you closing the socket on the server side?

I'm assuming you are effectively ending up with something similar to:

// Send EX
socket.BeginSend(...);
// Close socket
socket.Close();


Because the BeginSend is asynchronous (and the Close is not), the
Close may actually end up occurring prior to the send, thus you see
the receive of 0 length data on the client side. Prior to closing,
try calling socket.Shutdown(). That takes an enumeration value as a
parameter. I forget the name, but the value I believe should be
"both". That will force the socket to clear all receive/send buffers,
so you can then Close safely knowing that the EX got sent.

socket.BeginSend(...);
socket.Shutdown(EnumerationType.Both); // need to change
EnumerationType to the correct name)
socket.Close();

-Adam
And just realized I completely missed the target - you are rewriting the
CLIENT, I initially thought you were rewriting the server. Although, I
suppose a similar problem could still be occurring within the VB
server. It might be worth throwing a network analyzer (Wireshark or
something) onto the client machine, see if the VB server is in fact
sending the EX?

Also, what does your code look like that calls BeginReceive/EndReceive
on your client? Maybe something strange going on there.

-Adam
 
P

Peter Duniho

Steve said:
I'm using asynchronous TCP sockets in a C# client application which is
trying to match an old VB6 program behavior. When the server starts to
close (also written in VB6 using winsock) it sends an "EX" code to all
the connected clients telling them to close and then closes all the
client winsock connections. In VB, the client received the "EX" command
and began the shutdown process. However, in the C# program I
immediately get 0 length data from the server like it's closed the
connection before sending the "EX"... I never see the "EX". There is a
very small amount of time between sending the "EX" and closing the
socket, but I'm curious what exactly is happening. I would like to be
able to distinguish between a normal shutdown (receiving the "EX") and
simply loosing the connection. Without the "EX" I don't know whether to
try and reconnect or if I was supposed to shut down.

Can anyone give me a brief explanation of what is happing?

Adam's reply probably isn't too far off the mark.

For a graceful closure of the connection, your server should be calling
Socket.Shutdown() with the SocketShutdown.Send value. This indicates to
the remote endpoint that the caller is done sending, but leaves the
receive channel open to allow the remote endpoint to still send data back.

Then the remote endpoint will see a 0 return value from a read, but only
after all data sent has been received. At that point, the remote
endpoint can call Socket.Shutdown() with SocketShutdown.Both, and then
close its own socket. Of course, it could at any time prior have called
Socket.Shutdown() with SocketShutdown.Send, if for some reason it had
determined it was done sending too. That'd be okay.

In either case, the local side, having called SocketShutdown() with
SocketShutdown.Send, should still wait for the 0-byte receive from the
remote endpoint, to be sure that end is really done with the connection too.

If either end closes the socket without using the Shutdown() method,
then the connection is reset instead of closed gracefully, which can
interrupt any communications currently underway.

It's not true that there's a significant difference between using Send()
vs BeginSend() with respect to issue; the hard reset can actually
interrupt previously sent data in either case, because even Send() only
causes data to be buffered at the network driver level and depending on
the condition of the connection, may not have been sent by the time the
user code calls Close().

(In fact, calls to BeginSend() may manage to complete the operation at
the user-code level by the time the call returns, making the call
identical to a call to Send()…if this happens, you'll see the completion
callback being executed synchronously in the same thread on which
BeginSend() was called).

Anyway, the bottom line is that most likely your server is written
incorrectly, and is closing the socket without correctly waiting for the
remote endpoint to acknowledge the close.

Pete
 
S

Steve Ricketts

As Adam said in his second post, this is on the Client side that is C# and
Sockets. The server is VB6 and winsock... we'll get around to changing that
another time. Right now, it talks to both VB clients and C# clients over
the same winsock so I don't have the ability to use Socket.Shutdown. But,
when you said: "If either end closes the socket without using the
Shutdown() method, then the connection is reset instead of closed
gracefully, which can interrupt any communications currently underway" I
think that's what's going on... and makes sense.

sr
 
P

Peter Duniho

Steve said:
As Adam said in his second post, this is on the Client side that is C#
and Sockets.

"C# and Sockets" is understood, but irrelevant except with respect to
the specific API. As far as client-side goes, you've yet to prove that
the _problem_ is on the client side. You only know that it's the client
that _observes_ the problem.
The server is VB6 and winsock... we'll get around to
changing that another time.

You may not have that luxury, if you care about fixing this particular
problem.
Right now, it talks to both VB clients and
C# clients over the same winsock so I don't have the ability to use
Socket.Shutdown.

I don't know what API you're using in VB6, but if it's a socket API, it
must have a shutdown method.
But, when you said: "If either end closes the socket
without using the Shutdown() method, then the connection is reset
instead of closed gracefully, which can interrupt any communications
currently underway" I think that's what's going on... and makes sense.

If it makes sense to you, then you need to fix the server. Not the client.

Pete
 
S

Steve Ricketts

"C# and Sockets" is understood, but irrelevant except with respect to the
specific API. As far as client-side goes, you've yet to prove that the
_problem_ is on the client side. You only know that it's the client that
_observes_ the problem.

SR: Hey! I can't even prove my name half the time!! ;-) All I know is
that the VB Winsock clients see the "EX" from the server but the C# Sockets
client does not. That's essentially why I thought it was something on the
client side.
You may not have that luxury, if you care about fixing this particular
problem.

SR: I might see what I can do with the Winsock on the server. But rewriting
it would be a major problem.
I don't know what API you're using in VB6, but if it's a socket API, it
must have a shutdown method.

SR: Winsock is not an API it's a COM component. In all the years I've used
them, I don't recall seeing anything like a shutdown method. However, I've
been fooled before so I will see what I can find there. That would seem to
make the best sense if something like that exists.
SR: Overall, I'd tend to agree that there is something to do on the server
side. I'll see what I can find out.

Steve
 
P

Peter Duniho

Steve said:
SR: Hey! I can't even prove my name half the time!! ;-) All I know
is that the VB Winsock clients see the "EX" from the server but the C#
Sockets client does not. That's essentially why I thought it was
something on the client side.

Well, unfortunately, that's not a valid conclusion. In fact, it's quite
common to have a server bug that is seen by or affects some clients and
not others.
SR: I might see what I can do with the Winsock on the server. But
rewriting it would be a major problem.

Well, which is the bigger problem? Clients failing to receive the
termination or fixing the server (I doubt it'd need a complete rewrite)?

Note that the answer to that question isn't obvious. It is in fact
possible that you can fix the clients to handle the forced reset without
too much trouble. If they are seeing the reset (an exception would
occur on i/o operations), then you can just clean things up less
gracefully. If they are not, you might have to add some timeout logic.
But either way, you might find that preferable to messing with the server.

Or you might not. I can't say. You probably do need to fix _something_
though. Fact is, the client code needs to be able to handle a hard
reset anyway, even if the server is fixed to behave properly. Hard
resets can happen for other reasons and your code should be able to deal
with them.
SR: Winsock is not an API it's a COM component.

Winsock is an API. COM is an API. Any COM object is also an API. You
seem to be making distinctions where none exist.

Though, Winsock doesn't use COM at all. So if you're using COM, you
must be using some kind of Winsock wrapper implemented in COM that can
be used in VB6 (which wouldn't surprise me).
In all the years I've
used them, I don't recall seeing anything like a shutdown method.

I have a hard time believing someone at Microsoft would wrap Winsock in
COM and not include the shutdown method. If your COM wrapper comes from
somewhere else, well…anything's possible. :)

Pete
 
T

Tom Shelton

Well, unfortunately, that's not a valid conclusion. In fact, it's quite
common to have a server bug that is seen by or affects some clients and
not others.


Well, which is the bigger problem? Clients failing to receive the
termination or fixing the server (I doubt it'd need a complete rewrite)?

Note that the answer to that question isn't obvious. It is in fact
possible that you can fix the clients to handle the forced reset without
too much trouble. If they are seeing the reset (an exception would
occur on i/o operations), then you can just clean things up less
gracefully. If they are not, you might have to add some timeout logic.
But either way, you might find that preferable to messing with the server.

Or you might not. I can't say. You probably do need to fix _something_
though. Fact is, the client code needs to be able to handle a hard
reset anyway, even if the server is fixed to behave properly. Hard
resets can happen for other reasons and your code should be able to deal
with them.


Winsock is an API. COM is an API. Any COM object is also an API. You
seem to be making distinctions where none exist.

Though, Winsock doesn't use COM at all. So if you're using COM, you
must be using some kind of Winsock wrapper implemented in COM that can
be used in VB6 (which wouldn't surprise me).

Yeah, I think he is using the Winsock control from VB. It's sucky and
horrible monstrosity.
I have a hard time believing someone at Microsoft would wrap Winsock in
COM and not include the shutdown method. If your COM wrapper comes from
somewhere else, well?anything's possible. :)

If it's the winsock control, as I suspect, then yes it's from ms and no it
doesn't implement shutdown. Only the close method. No, close supposedly does
an orderly shutdown internally, but the winsock control is notoriously
unreliable. His salvation maybe that the winsock control does expose the os
socket handle, so he could always call the socket api's directly do do a
proper shutdown...
 
S

Steve Ricketts

Yes, it is the MS winsock control... that's what I meant by "COM Component".
Grabbing the socket's handle and using the API to do a better shutdown is a
good idea... I'll give that a go.

Thanks!

sr
 
A

Adam Clauss

Peter said:
It's not true that there's a significant difference between using
Send() vs BeginSend() with respect to issue; the hard reset can
actually interrupt previously sent data in either case, because even
Send() only causes data to be buffered at the network driver level and
depending on the condition of the connection, may not have been sent
by the time the user code calls Close().
That's interesting to know - I thought Send() would not return until the
data had actually been sent across the wire (otherwise, what purpose
does it's return value serve?)

-Adam
 
P

Peter Duniho

Adam said:
That's interesting to know - I thought Send() would not return until the
data had actually been sent across the wire (otherwise, what purpose
does it's return value serve?)

Its return value tells you how much data it could buffer. Sometimes,
there is not even enough space in the network driver's buffers to accept
all the data you are trying to send. This happens either when the user
code is sending a very large buffer all at once, or the network driver's
buffers are not being emptied fast enough.

The blocking Send() almost always just keeps retrying and won't return
until it's _buffered_ everything you asked it to. The only non-failure
exception to that would be if the socket is in non-blocking mode, in
which case a call to Send() could in fact return earlier, with a "would
block" error.

But the Send() method definitely does not wait until confirmation of
_receipt_ has happened. The data may not have even left the local PC
yet when Send() returns.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top