Linux and Xamarin socket problems

One of the requirements I set myself for my Socket library was that it should be as robust as possible about knowing that the other end of a connection had gone away. The .net socket objects are pretty bad for this, and the only way to be certain that the connection is up is to attempt to read or write its data stream.

At some point I will add an ping facility to the library to do that automatically for connections in Listening mode (that’s a mode I have created where sockets can send each messages asynchronously, not waiting for a reply, so apps can chatter to each other). I’ll have to do that to detect things like a network cable being pulled out, because there really is no way that a socket can detect that at all.

[Edit: The ping facility (aka Listening mode) is in the library now]

The facility built into a socket to show whether it is connected is the Connected property. This is almost completely useless, because it just tells you whether it was connected the last time you tried to use it, and you really want to know whether it still connected or whether something has happened to the far end in the meantime.

In Windows, it’s quite straightforward. You can get a list of the connections that the machine has, look for a connection with your socket’s local endpoint, and see whether its state is Established. For example,

var state = IPGlobalProperties.GetIPGlobalProperties()
                    .GetActiveTcpConnections()
                    .SingleOrDefault(x => x.LocalEndPoint.Equals(_client.Client.LocalEndPoint));
                if (state.State == TcpState.Established) return true;

The same thing should work unchanged in Mono on any platform, but it doesn’t. I found that, on Linux, whenever I made a connection, the client thought that its socket was closed. It’s not a bug in Linux, it’s a bug in Mono. Much googling eventually led me to Bugzilla, where someone reported the bug years ago. This is the enum in Linux

enum {
	TCP_ESTABLISHED = 1,
	TCP_SYN_SENT,
	TCP_SYN_RECV,
	TCP_FIN_WAIT1,
	TCP_FIN_WAIT2,
	TCP_TIME_WAIT,
	TCP_CLOSE,
	TCP_CLOSE_WAIT,
	TCP_LAST_ACK,
	TCP_LISTEN,
	TCP_CLOSING,	/* Now a valid state */
	TCP_NEW_SYN_RECV,

	TCP_MAX_STATES	/* Leave at the end! */
};

and this is the enum in .net

public enum TcpState {
    Unknown = 0,
    Closed = 1,
    Listen = 2,
    SynSent = 3,
    SynReceived = 4,
    Established = 5,
    FinWait1 = 6,
    FinWait2 = 7,
    CloseWait = 8,
    Closing = 9,
    LastAck = 10,
    TimeWait = 11,
    DeleteTcb = 12
}

By an odd coincidence, 1 means Established on Linux and Closed in .net, and I was seeing these established connections as closed on Linux. The relevant Mono code is on GitHub. The problem is in MibIPGlobalProperties.GetActiveTcpConnections. This is reading the IP connection info from /proc/net/tcp, which comes in looking like a text file, parsing it, and then setting the .net TcpState using the Linux TCP state number

 TcpState state = (TcpState) int.Parse (list [i] [3], NumberStyles.HexNumber);

D’Oh!

So I’ve worked around that by translating the states from one enum to the other (but only if I’m on Mono and Linux, of course) and that seems to work.

So the next thing was to test it on Xamarin.Android. This failed completely with a not implemented exception on UnixIPGlobalProperties.GetActiveTcpConnections. Have a look at that Mono code on GitHub again. Sure enough that method is not implemented. UnixIPGlobalProperties is meant to be overridden. The class with the Linux code in is MibIPGlobalProperties, but AndroidIPGlobalProperties derives directly from UnixIPGlobalProperties instead.

D’Oh AGAIN! I’ve found these bugs on Bugzilla and Xamarin forums. They were reported years ago.

There’s no easy way of getting into the Mono code to create an instance of MibIPGlobalProperties, because it’s internal (as it should be, of course). So to get this to work on Android I’ve had to chop out the parts of the class that I want, and call that when running on Android. (I’ve no idea what further hoops I may have to jump through to get this code to work on iOS).

Here is the class that I am using to get the info on Android. To the extent that I am using it, this seems to work. Note that I haven’t fixed the TcpState bug above in it, because I still need that fix in there for running on non-Xamarin Linux.

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.