Re: Socket weirdness
- From: "Dave Sexton" <dave@jwa[remove.this]online.com>
- Date: Mon, 18 Sep 2006 09:53:56 -0400
Hi William,
See some strange results on Send and peer with a closed Receive. Setup a
test harness. Server accepts socket, then just does a Shutdown.Receive
right away and waits. The client socket does both blocking Sends and
BeginSends to see the difference in behavior.
Results:
1) Using blocking Sends - The first send returns 10, which is the buffer
size. Future Sends, return 0 and no exception. This behavior seems not to
fly with the doco. The send should block until 10 bytes are sent to kernel
mode or throw an exception, *not return 0. Note the socket is in blocking
mode (default setting).
Has nothing to do with blocking sends. Reverse the order in which you call them, i.e. async before blocking, and you'll see the
same results. Also, the docs say that when the send method succeeds it is not an indication that the data was actually received.
About Send returning zero, see #3.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecomm5/html/wce50lrfsetsockoptWindowsSockets.asp
2) Using async BeginSend gives completely different results. The first
EndSend returns 10 as above. The second BeginSend returns a null
IAsyncResult and sets SocketError to "ConnectionReset". I find this
behavior very strange. BeginSend should never return a null IAsyncResult as
that FooBars the async pattern (it should always return IAsync or
exception). It should save the exception for the EndSend call. Second, why
does it set SocketError to ConnectionReset, when blocking Send does not seem
to care. The two behaviors should be consistent.
I agree about the null IAsyncResult being poorly designed, but you can simply check that it's not null before calling EndSend. A
null IAsyncResult indicates that something went wrong and that you should check the SocketError. Also, they are consistent. In my
testing both async and blocking Send use the ConnectionReset constant to indicate the same failure.
Both blocking Send and Async Send will return SocketError.ConnectionReset after the connection has been reset. In other words, it
doesn't matter which of the Sends, async or blocking, is executed first. Since the server has shutdown receiving on that socket the
first call to Send (async or blocking) will reset the connection. Subsequent Sends will fail, as you've witnessed. As the docs
stated, the first succeeding call to Send (either async or blocking) does not indicate that the server actually received the data.
For this reason you should design software that acknowledges transmission on a higher level, such as in a custom communications
protocol.
3) Also, what is the point of SocketError being an out parm? It should just
be included in the SocketException and thrown - no?
You have the choice of not using a SocketError argument by using an overload of Send that doesn't accept the out parameter. A
SocketException will be thrown for these overloads instead. Use the SocketException.SocketErrorCode property to retrieve the
SocketError from the exception.
The bottom line is that you must perform some operation on the Socket so that it can determine the state of the connection. The
state is not dynamic so that an action on one end point automatically affects the other. By shutting down Receive on the server end
point you are basically setting up a wall. Only by attempting to Send data will the socket update its internal state so that
subsequent calls will fail. It would be nice, however, if the first send failed as well but I see that from the docs this "design"
was by choice.
Here's your code revised in a manner that will hopefully illustrate what I've written above:
private static readonly EventWaitHandle waitForAsyncSend = new EventWaitHandle(false, EventResetMode.AutoReset);
private static void SocketTest()
{
// Server.
TcpListener l = new TcpListener(IPAddress.Any, 9001);
l.Start();
new Thread(
delegate()
{
using (Socket socket = l.AcceptSocket())
{
socket.Shutdown(SocketShutdown.Receive);
WriteLine("Server shutdown receive.");
waitForAsyncSend.WaitOne();
// expecting 4 blocks of 10 bytes each
WriteLine("Server about to poll for data");
// examine first batch
if (socket.Poll(8000000, SelectMode.SelectRead))
{
byte[] buffer = new byte[10];
try
{
int read = socket.Receive(buffer);
WriteLine("Server read bytes: " + read);
}
catch (SocketException ex)
{
if (ex.ErrorCode == 10053)
{
WriteLine("Server read error: " + ex.SocketErrorCode.ToString());
}
else
throw ex;
}
}
WriteLine("Closing client connection");
}
WriteLine("Server stopping");
l.Stop();
}).Start();
// Client.
byte[] buf = new byte[10];
using (Socket s = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp))
{
WriteLine("Blocking mode:{0}", s.Blocking);
s.Connect(IPAddress.Loopback, 9001);
Thread.Sleep(2000);
SocketError se = SocketError.Success;
int read = 0;
// Note the different results with async send.
IAsyncResult ar = s.BeginSend(buf, 0, buf.Length, SocketFlags.None, out se, null, null);
WriteLine("Non-blocking SocketError: " + se.ToString());
if (ar != null)
read = s.EndSend(ar); // ar is null.
WriteLine("Non-blocking bytes written to kernel:{0}", read);
waitForAsyncSend.Set();
Thread.Sleep(2000);
for (int i = 0; i < 3; i++)
{
read = s.Send(buf, 0, buf.Length, SocketFlags.None, out se);
WriteLine("Blocking bytes written to kernel:{0}\r\nSocketError:{1}", read, se);
Thread.Sleep(500);
}
Console.WriteLine("Click 'Enter' to exit");
Console.ReadLine();
}
}
private static readonly object sync = new object();
private static void WriteLine(string message)
{
lock (sync)
{
Console.WriteLine(message);
Console.WriteLine();
}
}
private static void WriteLine(string format, params object[] args)
{
lock (sync)
{
Console.WriteLine(format, args);
Console.WriteLine();
}
}
--
Dave Sexton
.
- Follow-Ups:
- Re: Socket weirdness
- From: William Stacey [MVP]
- Re: Socket weirdness
- References:
- Socket weirdness
- From: William Stacey [MVP]
- Socket weirdness
- Prev by Date: solution for dbase (*.dbf) jet c# (or vb) problem with ole or odbc (can't find table)
- Next by Date: Re: Socket weirdness
- Previous by thread: Re: Socket weirdness
- Next by thread: Re: Socket weirdness
- Index(es):
Relevant Pages
|