Re: Socket weirdness



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


.



Relevant Pages

  • Non-blocking and semi-blocking Sockets class.
    ... There is an issues with sockets blocking and how to handle it. ... that could mean the server closed a connection for whatever reason. ... The loop uses a separate timer from the socket timeout timer. ... Anyone can suggest a good state machine design in Java? ...
    (comp.lang.java.programmer)
  • Re: Socket weirdness
    ... Server accepts socket, ... The client socket does both blocking Sends and ... |> 2) Using async BeginSend gives completely different results. ... |> IAsyncResult and sets SocketError to "ConnectionReset". ...
    (microsoft.public.dotnet.framework)
  • Re: recv blocks although socket is ready
    ... of the members of the interface in my network layer have blocking ... Only one has non-blocking semantics. ... call any socket operation and rely on the socket to block. ... Now I have to implement blocking myself in all other ...
    (microsoft.public.win32.programmer.networks)
  • Re: Async socket & active connections
    ... The "blocking = true" in the disconnect phase seems to solve the ... Socket and TcpClient, ... blocking connection somewhere down the line. ... 300K connections, enough to start worrying... ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: recv blocks although socket is ready
    ... The problem is, that in blocking mode ... select sometimes reports the socket readable and then the following ... following call to recv() blocks. ...
    (microsoft.public.win32.programmer.networks)