RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Socket methods... am I just stupid?

Cheetah2003

Wanderer
Socket methods... am I just stupid?

Working on my RunUO Remote GUI thing...

So, basically, I'm doing async socket operations.

When I push a connect button on my form, I start by doing an async Dns.GetHostEntry to resolve the address of the server.

When it completes, I begin an async connect. Code:
Code:
        #region Connecting and DNS
        private void GetHostEntryCallback(IAsyncResult ar)
        {
            try
            {
                IPHostEntry e = Dns.EndGetHostEntry(ar);
                if (e.AddressList.Length == 0)
                    throw new Exception("No address associated with hostname.");
                m_Server.Address = e.AddressList[0];
                BeginInternalConnect();
            }
            catch (Exception e)
            {
                QueueMessage(String.Format("[!] Resolve failed: {0}", e.Message));
            }
        }

        public void BeginConnect()
        {
            IPAddress addr = null;
            try
            {
                addr = IPAddress.Parse(m_Server.ServerHostName);
            }
            catch
            {
                addr = null;
            }

            if (addr == null)
            {
                QueueMessage(String.Format("[+] Resolving {0}...", m_Server.ServerHostName));
                Dns.BeginGetHostEntry(m_Server.ServerHostName, new AsyncCallback(GetHostEntryCallback), null);
            }
            else
            {
                m_Server.Address = addr;
                BeginInternalConnect();
            }
        }

        public void BeginInternalConnect()
        {
            QueueMessage(String.Format("[+] Connecting to {0}...", m_Server.Address.ToString()));
            IPEndPoint hostPoint = new IPEndPoint(m_Server.Address, m_Server.ServerPort);

            try
            {
                m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                m_Socket.BeginConnect(hostPoint, new AsyncCallback(ConnectCallback), null);
            }
            catch (Exception e)
            {
                QueueMessage(String.Format("[!] Can't connect: {0}", e.Message));
                m_Socket.Close();
                m_Socket = null;
            }
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                m_Socket.EndConnect(ar);
                m_Connected = true;
                QueueMessage(String.Format("[+] Connected to {0} ({1})", m_Server.ServerHostName, m_Server.Address));
                m_Server.ConnectedAt = DateTime.Now;
            }
            catch (SocketException e)
            {
                m_Socket.Close();
                m_Socket = null;
                QueueMessage(String.Format("[!] Can't connect: {0}", e.Message));
            }
            catch
            {
                m_Socket.Close();
                m_Socket = null;
                QueueMessage("[!] Unknown error.  Can't connect!");
            }
        }
        #endregion

Entry point is BeginConnect(), which is called from another class.

Now, the problem is, sometimes I get this really annoying SocketException, goes like "The attempted operation is not supported for the type of object referenced."

The description from MSDN goes: "Operation not supported. The attempted operation is not supported for the type of object referenced. Usually this occurs when a socket descriptor to a socket that cannot support this operation is trying to accept a connection on a datagram socket."

It doesn't make a lot of sense. I'm not using a datagram socket, obviously. When I push connect the first time, I always get that exception thrown from BeginConnect, but if I push it again right after that, it works.

Getting it out of other WinSock functions too, like Disconnect. Really puzzling. So, am I just stupid? Something I'm not doing right?

I've tried it every different way I could think of. First I tried changing the way I use BeginConnect, by using a string to let it resolve itself, same thing. I tried a IPAddress instead of IPEndPoint. Same thing.

I thought maybe calling the BeginInternalConnect from the DNS callback might be a problem, so I tried having my worker thread do the call to BeginInternalConnect when the DNS was finished. Same thing.

Tried making ConnectCallback static and passing a reference to the object via AsyncResult, so I can access data from the instance. Same thing.

Any advise or insight would be greatly appreciate!
 

noobie

Wanderer
do not call another async method within an async method or delegate. AFAIK, it is a bug with .NET 2.0 about thread pooling mechanism.

you might remove second async callback method

let me know whether it works or not..
 

noobie

Wanderer
by the way, earlier I said it is a bug but it might also be considered as a design issue.

here is the problem about the code:

when you call an async method, the process is handled by a worker thread from thread pool. as soon as the process is completed, the worker thread returns to pool and callback method is invoked in the "caller thread"

so if you invoke another async method/delegete within an async method/delegate, here is what happens sometimes:
if the first async method is completed and the worker thread returned to thread pool already when the inner (second) async method is finished, it tries to get first worker thread (for callback method). but by the time then, it might be used for another process since it is a thread pool.
 

Cheetah2003

Wanderer
noobie;653697 said:
by the way, earlier I said it is a bug but it might also be considered as a design issue.

here is the problem about the code:

when you call an async method, the process is handled by a worker thread from thread pool. as soon as the process is completed, the worker thread returns to pool and callback method is invoked in the "caller thread"

so if you invoke another async method/delegete within an async method/delegate, here is what happens sometimes:
if the first async method is completed and the worker thread returned to thread pool already when the inner (second) async method is finished, it tries to get first worker thread (for callback method). but by the time then, it might be used for another process since it is a thread pool.

That makes sense and all, but I did try executing the BeginInternalConnect from my own worker thread when the Dns resolve is signaled completed, and the results were the same. But I'm certain it has something to do with both async operations happening consecutively, because in cases where a resolve is not required, it doesn't throw exceptions.

I should also note, when I coded it to use the BeginConnect overload that takes a string and does a resolve on it's own, thereby eliminating my async Dns query, the problem is still present.

Just not sure how to go about fixing it. :p
 

Cheetah2003

Wanderer
noobie;653703 said:
just use Socket#Connect instead of Socket#BeginConnect.

Not an option. I don't want my program blocked while the tcp/ip stack is doing stuff, like resolving, or connecting. That's just annoying.

The article is definitely interesting, but it applies to .NET 1.1, and I'm working with 2.0, and these methods are new to 2.0.

At any rate, thanks for yer help. I'll get it figured out. At worst, it's annoying, at best, it doesn't happen.

Not pouring much more time into it, right now. I can get it to connect, want to start getting the real meat of my app coded and functioning. :)
 

mordero

Knight
well if you dont want it to be blocked, you can always create your own threads to do some of this stuff, then you would only destroy the thread once all of the async stuff is finished (that way you arent using async methods inside of other async methods)
 

noobie

Wanderer
it shouldnt be blocked when you use Connect instead of BeginConnect.

in UI thread, you already call an async delegate, which will not block UI thread untill its callback method is done.

why do you need to use async method for Connect? It doesnt matter whether your thread (its a worker thread in this case, not UI thread, be careful) is blocked or not. and given your code, your worker thread doesnt do anything after calling BeginConnect, so there is no need to be using async method there.
 

Cheetah2003

Wanderer
noobie;653929 said:
it shouldnt be blocked when you use Connect instead of BeginConnect.

in UI thread, you already call an async delegate, which will not block UI thread untill its callback method is done.

why do you need to use async method for Connect? It doesnt matter whether your thread (its a worker thread in this case, not UI thread, be careful) is blocked or not. and given your code, your worker thread doesnt do anything after calling BeginConnect, so there is no need to be using async method there.

Well, if you look at the code, you can see, regardless of what happens, BeginConnect returns almost immediately. This is important, as it allows my form to continue to do things, like display connection progress, etc. Basically, the form, on a menu click, calls BeginConnect. If I use Connect there, then the form will be unresponsive until Connect returns. That is precisely what I'm trying to avoid. Using the callbacks from async stuff lets my form continue to function, while the underlying worker threads do their thing, eventually hitting a callback delegate that informs my form connection has been established. (i've changed that code quite a bit, but still having this problem.)

mordero there has a good idea though, use my own threading to do such things. I may just go about doing that, that would simplify everything since I'd know exactly what's going on. Thanks mordero. :)
 

noobie

Wanderer
OK, I think I didnt make it clear.

Connect part will be executed in a worker thread which is created by async delegate. So it shouldnt block your UI thread whether it waits one or ten seconds.

to see exactly what happens in your code.

Write a debug statements like this in the beginning of each method:
QueueMessage("<MethodName> "+Thread.GetCurrentThread().IsThreadPoolThread+" "+Thread.GetCurrentThread().GetHashCode());
 
Top