Connect stalls when invalid IP

Topics: Admin Tool, Developers
Jan 31, 2012 at 9:32 PM

Hi,

If I set IP=1 and Port=1 and then RCon.Connect () ... then the connect stalls and never times out (monotouch).

Shouldn't it timeout?

Thanks :)

Mojo

Coordinator
Feb 1, 2012 at 2:30 AM

Well, the behavior for this works as expected for regular .NET (and therefore probably Mono), but MonoTouch very different from Mono. One thing you could try is using ConnectSync and handling SocketExceptions yourself.

Feb 1, 2012 at 10:12 AM

I just tried a local IP ... 10.0.0.123 and it stalls too also when using ConnectSync. So iOS app will crash on an invalid IP. SocketException is never raised.

Is it a bug in BF3Rcon.NET?

Coordinator
Feb 1, 2012 at 3:37 PM

First, this isn't a BF3Rcon.NET bug, since the behavior for invalid IPs and failed connections works as expected in .NET and (probably) Mono. Also note that successfully connecting to a non-RCON host is undefined behavior.

After testing with Mono for Android, I've found that using ConnectSync on an invalid ip (like in your first example) works fine. However, failing to connect to an existing host seems more problematic. It normally takes .NET a few seconds to realize a connection can't be established when a host ignores it, but it's taking Mono for Android a couple minutes to do this. This isn't something that can really be solved.

However, I recall you using Mono for iOS (i think). In Android, an exception was thrown for invalid parameters, but you say your application just hung; this is strange because Connect is asynchronous and therefore non-blocking, and, obviously, the behavior between the two is different. Sadly, I can't do any testing for iOS. The best thing you can do is either have your users be pretty sure the server information is correct or ask the Xamarin people for help.

Feb 1, 2012 at 7:20 PM

I implemented a timer, so if async is not connected within 5 sec, it cancels the rcon .... But ... I'm not sure how I can tell BF3Rcon.Net to stop whatever it's doing and just close and reset everything ... so it's state is like when it's created ... is that possible?

Thanks :)

Coordinator
Feb 1, 2012 at 10:42 PM

You can use Disconnect.

Feb 2, 2012 at 12:31 PM
Edited Feb 2, 2012 at 12:35 PM

Hi Timiz0r,

(I'm not good at sockets at all! :)

I looked around on the internet and found this:

 

static void Connect(RconClient client)
{
    AutoResetEvent connectDone = new AutoResetEvent(false);
    client.Socket.BeginConnect(client.Address, client.Port, new AsyncCallback(delegate(IAsyncResult ar) { connectDone.Set(); ConnectCallback(ar); }), client);

    if (!connectDone.WaitOne(1000))
    {
        // Do "connection failed stff" :)
    }
}

 

I tried implemented it in your library and it worked just fine in iOS. Could you impletment it? It works in windows too (dunno about Android).

You could even implement a TimeOut value ... (!connectDone.WaitOne(timeOut)) ... so we can control the timeout our self.

I'm not sure what to do in the "// Do "connection failed stff" :)" section. Disconnect? Raise an event?

Okay - that was my two cents :)

Mojo

 

Update: Using WaitOne seams to break the Async ... hmmm.

Coordinator
Feb 2, 2012 at 2:03 PM

While I'm not willing to implement this outright because having an exception showing why a connection fails is important, I could add #if directives around that code so you can enable and build with those features yourself.

Also, is what's breaking this possibly an ObjectDisposedException?

And actually, now that I think about it, you should be able to implement this without changing the library's code. I just have to fix one related bug first, then I'll write up some code that will hopefully work.

Coordinator
Feb 2, 2012 at 2:41 PM
Edited Feb 2, 2012 at 2:42 PM

Here's the code. I haven't tested it, though.
Also, Connect will return true on success. If the connection attempt times out or fails, the client will disconnect and the method will return false.

        int timeout = 10000;
        ManualResetEventSlim Mres = new ManualResetEventSlim(false);
        //RconClient client;
        
        bool Connect()
        {
            Mres.Reset(); //make sure it blocks
 
            client.Connect();
 
            if (!Mres.Wait(timeout) || !client.IsConnected)
            {
                // ^ timeout              ^ connection failed
                client.Disconnect();
                return false;
            }
 
            return true;
        }
        //also have the client use the event methods below
 
        void client_Connected1(object sender, EventArgs e)
        {
            Mres.Set();
        }
 
        void client_ConnectError(object sender, ConnectErrorEventArgs e)
        {
            Mres.Set();
        }
Feb 2, 2012 at 4:21 PM

The "Wait" ... won't it block the UI? Currently I'm showing a spinning ajax to show I'm logging on.

Feb 2, 2012 at 5:44 PM

I updated to your last source and tried making this test:

 

using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Threading ;

namespace BF3RConTest
{
	public partial class frmTestConnect : UIViewController
	{
		
		System.Net.Battlefield3.RconClient client;
		int timeout = 5000;
       		ManualResetEventSlim Mres = new ManualResetEventSlim(false);
		
		public frmTestConnect () : base ("frmTestConnect", null)
		{
		}
		
		public override void DidReceiveMemoryWarning ()
		{
			base.DidReceiveMemoryWarning ();
		}
		
		public override void ViewDidLoad ()
		{
			base.ViewDidLoad ();
			client = new System.Net.Battlefield3.RconClient ();
			
			this.cmdLogOn.TouchUpInside += delegate(object sender, EventArgs e) {
				Mres.Reset();
				client.Address = "x.x.x.x";
				client.Port = 25200;
				Console.WriteLine ("Connecting...");
				client.Connect (); 
				if (!Mres.Wait(timeout) || !client.IsConnected)
	            		{
	                		client.Disconnect();
	                		Console.WriteLine ("Connection failed!");
	            		}
			};
			
			this.cmdLogoff.TouchUpInside += delegate(object sender, EventArgs e) {
				Console.WriteLine ("Disconnecting...");
				client.Disconnect ();
				Console.WriteLine ("Disconnected");
			};
			
			client.Connected += delegate(object sender, EventArgs e) {
				Mres.Set();
				Console.WriteLine ("Connected");
				Console.WriteLine ("Logging on...");
				client.LogOn ("test");
			};
			
			client.ConnectError += delegate(object sender, System.Net.Battlefield3.ConnectErrorEventArgs e) {
				Mres.Set();
				Console.WriteLine ("Connect error: " + e.ToString ());
			};
			
			client.LoggedOn += delegate(object sender, EventArgs e) {
				Console.WriteLine ("LoggedOn");
				Console.WriteLine ("Player count: " + client.Players.Count);
			};
		}
		
		public override void ViewDidUnload ()
		{
			base.ViewDidUnload ();
			ReleaseDesignerOutlets ();
		}
		
		public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
		{
			return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
		}
	}
}


But logging on and off a couple of times, suddenly throws this error:
Unhandled Exception: System.InvalidOperationException: out of sync
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].VerifyState () [0x00028] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:939 
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].MoveNext () [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:862 
  at System.Net.Battlefield3.RconClient.Reset () [0x0008e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:794 
  at (wrapper remoting-invoke-with-check) System.Net.Battlefield3.RconClient:Reset ()
  at System.Net.Battlefield3.RconClient.Disconnect (System.Net.Battlefield3.RconClient client, System.String message, SocketError error, Boolean doEvent) [0x0002e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:774 
  at System.Net.Battlefield3.RconClient.Disconnect () [0x00000] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:751 
  at BF3RConTest.frmTestConnect.m__1 (System.Object sender, System.EventArgs e) [0x0000a] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/frmTestConnect.cs:45 
  at MonoTouch.UIKit.UIControlEventProxy.Activated () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIControl.cs:30 
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29 
  at BF3RConTest.Application.Main (System.String[] args) [0x00000] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/Main.cs:17 
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: out of sync
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].VerifyState () [0x00028] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:939 
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].MoveNext () [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:862 
  at System.Net.Battlefield3.RconClient.Reset () [0x0008e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:794 
  at (wrapper remoting-invoke-with-check) System.Net.Battlefield3.RconClient:Reset ()
  at System.Net.Battlefield3.RconClient.Disconnect (System.Net.Battlefield3.RconClient client, System.String message, SocketError error, Boolean doEvent) [0x0002e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:774 
  at System.Net.Battlefield3.RconClient.Disconnect () [0x00000] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:751 
  at BF3RConTest.frmTestConnect.m__1 (System.Object sender, System.EventArgs e) [0x0000a] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/frmTestConnect.cs:45 
  at MonoTouch.UIKit.UIControlEventProxy.Activated () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIControl.cs:30 
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29 
  at BF3RConTest.Application.Main (System.String[] args) [0x00000] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/Main.cs:17 

Application Terminated

Any idea?

:)

Coordinator
Feb 2, 2012 at 8:17 PM
MojoDK wrote:
But logging on and off a couple of times, suddenly throws this error:
Unhandled Exception: System.InvalidOperationException: out of sync
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].VerifyState () [0x00028] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:939 
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].MoveNext () [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:862 
  at System.Net.Battlefield3.RconClient.Reset () [0x0008e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:794 
  at (wrapper remoting-invoke-with-check) System.Net.Battlefield3.RconClient:Reset ()
  at System.Net.Battlefield3.RconClient.Disconnect (System.Net.Battlefield3.RconClient client, System.String message, SocketError error, Boolean doEvent) [0x0002e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:774 
  at System.Net.Battlefield3.RconClient.Disconnect () [0x00000] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:751 
  at BF3RConTest.frmTestConnect.m__1 (System.Object sender, System.EventArgs e) [0x0000a] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/frmTestConnect.cs:45 
  at MonoTouch.UIKit.UIControlEventProxy.Activated () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIControl.cs:30 
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29 
  at BF3RConTest.Application.Main (System.String[] args) [0x00000] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/Main.cs:17 
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: out of sync
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].VerifyState () [0x00028] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:939 
  at System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,System.Net.Battlefield3.SynchronousReadState].MoveNext () [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:862 
  at System.Net.Battlefield3.RconClient.Reset () [0x0008e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:794 
  at (wrapper remoting-invoke-with-check) System.Net.Battlefield3.RconClient:Reset ()
  at System.Net.Battlefield3.RconClient.Disconnect (System.Net.Battlefield3.RconClient client, System.String message, SocketError error, Boolean doEvent) [0x0002e] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:774 
  at System.Net.Battlefield3.RconClient.Disconnect () [0x00000] in /Users/mojo/MonoDevelop/Projects/Mojo/Libraries/BF3RCon/BF3RCon/RconClient.cs:751 
  at BF3RConTest.frmTestConnect.m__1 (System.Object sender, System.EventArgs e) [0x0000a] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/frmTestConnect.cs:45 
  at MonoTouch.UIKit.UIControlEventProxy.Activated () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIControl.cs:30 
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29 
  at BF3RConTest.Application.Main (System.String[] args) [0x00000] in /Users/mojo/MonoDevelop/Projects/Test/BF3RConTest/BF3RConTest/Main.cs:17 

Application Terminated

 

Any idea?

:)

I've just fixed this (hopefully) with the latest code. The exception probably occurred when a receive finished or a new one started while disconnecting/resetting.

MojoDK wrote:

The "Wait" ... won't it block the UI? Currently I'm showing a spinning ajax to show I'm logging on.

It does, but so does the other code you showed me. If you don't want to block the main thread, you'll have to use a timer.

Feb 2, 2012 at 8:37 PM

Hi Timiz0r,

Cool seamed to do the trick.

After a  few connect/disconnect, I got this error:

Unhandled Exception: System.ObjectDisposedException: The object was used after being disposed.  at System.Net.Sockets.Socket.EndSend (IAsyncResult asyncResult, System.Net.Sockets.SocketError& errorCode) [0x00016] in /Developer/MonoTouch/Source/mono/mcs/class/System/System.Net.Sockets/Socket_2_1.cs:2002   at System.Net.Sockets.Socket.EndSend (IAsyncResult result) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/System/System.Net.Sockets/Socket_2_1.cs:1985   at System.Net.Battlefield3.RconClient.SendCallback (IAsyncResult ar) [0x00000] in /Users/Mojo/MonoDevelop/Projects/Mojo/GamezAdmin/BF3RCon/BF3RCon/RconClient.cs:677 

Hope you ain't getting tired of me :)

Mojo

Coordinator
Feb 2, 2012 at 8:58 PM

That bug is caused when a send is done right as a disconnect is also happening. I've now fixed that.

Also, I thank you for showing me all of these bugs; it's helping to make everything more stable :D

Feb 2, 2012 at 9:19 PM
Timiz0r wrote:

Here's the code. I haven't tested it, though.
Also, Connect will return true on success. If the connection attempt times out or fails, the client will disconnect and the method will return false.

        int timeout = 10000;
        ManualResetEventSlim Mres = new ManualResetEventSlim(false);
        //RconClient client;
        
        bool Connect()
        {
            Mres.Reset(); //make sure it blocks
 
            client.Connect();
 
            if (!Mres.Wait(timeout) || !client.IsConnected)
            {
                // ^ timeout              ^ connection failed
                client.Disconnect();
                return false;
            }
 
            return true;
        }
        //also have the client use the event methods below
 
        void client_Connected1(object sender, EventArgs e)
        {
            Mres.Set();
        }
 
        void client_ConnectError(object sender, ConnectErrorEventArgs e)
        {
            Mres.Set();
        }

If I try this code on an invalid IP, then when doing the "client.Disconnect ()", I get this error:

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object  at System.Net.Battlefield3.RconClient.ConnectCallback (IAsyncResult ar) [0x0000c] in /Users/Mojo/MonoDevelop/Projects/Mojo/GamezAdmin/BF3RCon/BF3RCon/RconClient.cs:575   at System.Net.Sockets.Socket+SocketAsyncResult.<DoMConnectCallback>m__B (System.Object _) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/System/System.Net.Sockets/Socket_2_1.cs:180 

And then I can't connect to a valid IP.

On an unfinished BeginConnect, calling Disconnect() seams to fail at line 575 ... socket is null.

Any idea?

Coordinator
Feb 2, 2012 at 9:31 PM

I had forgotten that the asynchronous connect will still try to complete even after the socket is closed. I've now fixed this bug, as well.

Feb 3, 2012 at 6:19 AM

If you first try to connect to an invalid IP, when it times out you do the Disconnect() and after this, you try to connect to a valid IP - then it will not connect. :(

Any idea?

Here's my test code (I first click cmdLogonInvalid and when it times out, I click cmdLogonValid ... and nothing happens):

*hmmm I can't insert Code Snippet here in codeplex suddently - can't select language or press insert*

Tell me if you need my code and I will mail it to you. :)

Coordinator
Feb 3, 2012 at 1:34 PM

You can always use something like pastebin to send the code.

Feb 3, 2012 at 3:17 PM

Forget my stupid post (Today at 7:19 AM) - I had a bug in my code ... sry :)

I just had an error in line 318 ... saying that the name already exist in the dictionary. I didn't get to capture the stacktrace. 

:)