I see what they did with the two version of send now. Send with the "out"
error overload should probably be changed to "bool TrySend(...)" to match
with current standards. The null IAsyncResult thing still bugs me. The
other thing we discover here via imperitive evidence is that
shutdown.receive will never be sent by server unless it can piggyback on an
ACK reply or an outgoing message. Something I must remember. Cheers.
--
William Stacey [MVP]
| 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/d...mm5/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
|
|