Sockets and non-blocking connect

M

Michi Henning

Hi,

I'm using a non-blocking connect to connect to a server.
Works fine -- the server gets and accepts the connection.
However, once the connection is established, I cannot
retrieve either the local or the remote endpoint from
the client-side socket.

The *really* strange thing is that Socket.LocalEndPoint is null.
According to the doc, that's impossible: reading the LocalEndPoint
or RemoteEndPoint property should either throw an exception or
give me a proper endpoint.

I've attached two very simple code snippets below. The server
simply sits and waits for an incoming connection on port 12345
on localhost. Once it has accepted an incoming connection, the
server sleeps for 10 seconds and then exits.

On the client side, I put the socket in non-blocking mode, initiate
the connection, and then use poll to wait for the socket descriptor
to become writeable (which indicates the the connection was completed).
At that point, LocalEndPoint and RemoteEndPoint are null, even though
the connection was successfully established (as evidenced by the output
from the server).

To run this, start the server in a window and the client in another window.

This looks like a bug in the .NET socket implementation to me. (The
equivalent code in C++ works fine.)

Can anyone help me out with this?

Thanks,

Michi.


using System;
using System.Net;
using System.Net.Sockets;

class Server
{
static void Main(string[] args)
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

s.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345));
s.Listen(1);
Socket c = s.Accept(); // Blocks until we get an incoming connection
Console.WriteLine((IPEndPoint)c.LocalEndPoint);
Console.WriteLine((IPEndPoint)c.RemoteEndPoint);
System.Threading.Thread.Sleep(10000);
}
}


using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

class Client
{
static void Main(string[] args)
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

s.Blocking = false; // We want to use non-blocking connect

try
{
s.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345)); // Initiate connection
}
catch (Win32Exception ex)
{
if (ex.ErrorCode == 10035) // WSAEWOULDBLOCK is expected, means connect is in progress
{
bool ready;
int repeatCount = 3;
do
{
ready = s.Poll(1000000, SelectMode.SelectWrite); // Wait until connection is complete
} while (!ready && --repeatCount > 0);
if (!ready)
{
Console.WriteLine("Connect failed");
Environment.Exit(1);
}
}
else
{
Console.WriteLine(ex);
Environment.Exit(1);
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex);
Environment.Exit(1);
}
IPEndPoint lep = (IPEndPoint)s.LocalEndPoint;
Debug.Assert(lep != null); // Assertion fails!!!

IPEndPoint rep = (IPEndPoint)s.RemoteEndPoint;
Debug.Assert(rep != null); // Assertion fails!!!

Environment.Exit(0);
}
}
 
M

Michi Henning

Michi said:
This looks like a bug in the .NET socket implementation to me. (The
equivalent code in C++ works fine.)

I'm quite sure that this is a bug in the implementation of the Socket
class because I can get things to work by stepping down to the native API
with platform invoke.

Add the following definitions to the client:

[StructLayout(LayoutKind.Sequential)]
private struct in_addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public byte[] sin_addr;
}

[StructLayout(LayoutKind.Sequential)]
private struct sockaddr
{
public short sin_family;
public ushort sin_port;
public in_addr sin_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] sin_zero;
}

[DllImport("wsock32.dll")]
private static extern int getsockname(IntPtr s, ref sockaddr name, ref int namelen);

[DllImport("wsock32.dll")]
private static extern int getpeername(IntPtr s, ref sockaddr name, ref int namelen);

[DllImport("ws2_32.dll")]
private static extern IntPtr inet_ntoa(in_addr a);

[DllImport("ws2_32.dll")]
private static extern ushort ntohs(ushort netshort);

Then, at the end of the client, instead of reading the LocalEndPoint
and RemoteEndPonit properties, insert the following:

IntPtr socket = s.Handle;
sockaddr addr = new sockaddr();
int addrLen = 16;

getsockname(socket, ref addr, ref addrLen);
IntPtr str = inet_ntoa(addr.sin_addr);
Console.WriteLine(Marshal.PtrToStringAnsi(str) + ":" + ntohs(addr.sin_port));

getpeername(socket, ref addr, ref addrLen);
IntPtr str = inet_ntoa(addr.sin_addr);
Console.WriteLine(Marshal.PtrToStringAnsi(str) + ":" + ntohs(addr.sin_port));

This correctly prints the IP address and port number of the local and remote endpoint.


Cheers,

Michi.
 
R

Rich Blum

Michi Henning said:
Hi,

I'm using a non-blocking connect to connect to a server.
Works fine -- the server gets and accepts the connection.
However, once the connection is established, I cannot
retrieve either the local or the remote endpoint from
the client-side socket.

Michi -

The reason you can not retrieve the local or remote endpoints is
because there is no connection. When the socket is set to
non-blocking, the Connect() method throws a SocketException (only for
TCP connections), since the connection can not be established without
blocking. From the Socket class library page:

"The Connect method will block, unless you specifically set the
Blocking property to false prior to calling Connect. If you are using
a connection-oriented protocol like TCP and you do disable blocking,
Connect will throw a SocketException because it needs time to make the
connection."

Thus the connection is never established, and the local and remote
endpoint values are not set. If you do not set the Blocking property
to false, your code works fine.

If you do not want to block on the Connect(), try using the async
BeginConnect() version instead. Hope this helps shed some light on
your problem.

Rich Blum - Author
"C# Network Programming" (Sybex)
http://www.sybex.com/sybexbooks.nsf/Booklist/4176
"Network Performance Open Source Toolkit" (Wiley)
http://www.wiley.com/WileyCDA/WileyTitle/productCd-0471433012.html
 
M

Michi Henning

The reason you can not retrieve the local or remote endpoints is
because there is no connection.
No.

When the socket is set to
non-blocking, the Connect() method throws a SocketException (only for
TCP connections), since the connection can not be established without
blocking. From the Socket class library page:

"The Connect method will block, unless you specifically set the
Blocking property to false prior to calling Connect. If you are using
a connection-oriented protocol like TCP and you do disable blocking,
Connect will throw a SocketException because it needs time to make the
connection."

Right. The man page says that. It also says that, if I connect in non-blocking
mode, the connection attempt is initiated, and that I should use poll() to
check
when the connection is established. Look at the code I sent -- it uses poll()
after the initial call to Connect() to wait until the socket becomes ready
for writing, which it does.
Thus the connection is never established, and the local and remote
endpoint values are not set.

No. The connection is indeed established. If you actually run the code I sent,
you will see the server receiving the incoming connection request
and correctly printing the local and remote endpoint.
If you do not set the Blocking property
to false, your code works fine.

Yes, but it also should work in non-blocking mode.

Have a look at my follow-up post. By using the native getsockname()
and getpeername() calls via platform invoke, the very same example
works just fine. The problem is definitely in the .NET Socket class
implementation.
If you do not want to block on the Connect(), try using the async
BeginConnect() version instead. Hope this helps shed some light on
your problem.

I considered that, but it doesn't solve my problem. What I need is the
connection attempt to be aborted after a set amount of time, not a
connection attempt that may block indefinitely (and tie up another
thread in the bargain).

Cheers,

Michi.
 
F

Feroze [MSFT]

Hi!

This seems to be a bug in the framework, and I am following up with our
developers on this. However, I had a question for you. Why are you doing a
non-blocking connect and polling for the connect to succeed ? WHy dont you
use a BeginConnect./EndConnect() async pattern, and that will avoid this
issue totally.

feroze.

--
Remove "user" from the email address to reply to the author.

This posting is provided "AS IS" with no warranties, and confers no rights

Use of included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm
 
M

Michi Henning

Feroze said:
Hi!

This seems to be a bug in the framework, and I am following up with our
developers on this. However, I had a question for you. Why are you doing a
non-blocking connect and polling for the connect to succeed ? WHy dont you
use a BeginConnect./EndConnect() async pattern, and that will avoid this
issue totally.

BeginConnect()/EndConnect() have two problems:

- They tie up a thread for the duration of the connection attempt (which
may take quite a while).

- There is no way to adjust the timeout for the connection.

I'm in a situation where I want to establish a connection and
wait for a specified time for the attempt to succeed.
Even assuming that I could set a timeout for BeginConnect()
(which I can't), the async pattern then forces me to suspend
the calling thread after calling BeginConnect() and then wake up that
thread again from within the callback when it finally arrives.
That's a lot of hoops to jump through just to wait for a set amount of
time and also wastes an additional thread.

The code is a lot clearer if it stays all in one thread:

- put socket in non-blocking mode

- call Connect()

- catch SocketException and test for WSAEWOULDBLOCK

- call Select() with timeout

- Select() either times out or indicates that the connection was established

- if timeout, return error

- else get local and remote endpoint

Cheers,

Michi.
 
C

Chad Z. Hower aka Kudzu

Michi Henning said:
- They tie up a thread for the duration of the connection attempt (which
may take quite a while).

- There is no way to adjust the timeout for the connection.

Use a secondary thread to do the connect. Thats what Indy has an option to
do.
I'm in a situation where I want to establish a connection and
wait for a specified time for the attempt to succeed.
Even assuming that I could set a timeout for BeginConnect()
(which I can't), the async pattern then forces me to suspend
the calling thread after calling BeginConnect() and then wake up that
thread again from within the callback when it finally arrives.
That's a lot of hoops to jump through just to wait for a set amount of
time and also wastes an additional thread.

The code is a lot clearer if it stays all in one thread:

- put socket in non-blocking mode

- call Connect()

- catch SocketException and test for WSAEWOULDBLOCK

- call Select() with timeout

- Select() either times out or indicates that the connection was
established

- if timeout, return error

- else get local and remote endpoint

Cheers,

Michi.



--
Chad Z. Hower (a.k.a. Kudzu) - http://www.hower.org/Kudzu/
"Programming is an art form that fights back"


ELKNews - Get your free copy at http://www.atozedsoftware.com
 
F

Feroze [MSFT]

Thanks a lot for this information. We appreciate the information your
provided for this issue. I will follow up with the developers.

Thanks a lot for reporting this bug.

feroze
 

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