Sign in to follow this  
John H

[.net] C# .NET v1.1 - More Remoting Woes - Updated

Recommended Posts

After my problems with remoting yesterday, someone suggested configuring my remoting object via configuration files, instead of programmatically. Now that I've changed to this approach, the clients in my chat program aren't receiving messages from each other. All I've done is move the code to configure the remoting object, into the configuration file and nothing else. I have a feeling I'm not initialising the remoting object properly, and I was hoping someone could explain why what I'm doing is incorrect. Here's the config files: Server:
<configuration>
	<system.runtime.remoting>
		<application>
		<service>
			<wellknown 
				mode="Singleton"
				type="ChatRemoter, ChatRemoter"
				objectUri="Chat"
			/>
		</service>
		<channels>
		<channel ref="http" port="80">
			<serverProviders>
				<formatter ref="binary" typeFilterLevel="Full" />
			</serverProviders>
		</channel>
		</channels>
		</application>
	</system.runtime.remoting>
</configuration>




Client:
<configuration>
	<system.runtime.remoting>
		<application>
		<client>
			<wellknown 
				type="ChatRemote.ChatRemoter, ChatRemoter"
				url="http://localhost:80/Chat"
			/>
		</client>
		<channels>
			<channel ref="http" port="0">
				<clientProviders>
					<formatter ref="binary" />
				</clientProviders>
			</channel>
		</channels>
		</application>
	</system.runtime.remoting>
</configuration>




Finally, here is how I am creating an instance of the remoting object:
remoteObj = new ChatRemoter();




I've also tried to use Activator.GetObject() but it throws an exception, saying, "File or assembly name, ChatRemoter could not be found." The confusing thing for me there, is I know the assembly isn't called ChatRemoter, it's called ChatRemote, so I don't know why it's looking for that. I've even tried changing the .config files to no avail. Edit: Here's how I previously created the remoting object, which worked fine.
remoteObj = (ChatRemoter) Activator.GetObject(
	typeof(ChatRemote.ChatRemoter),
	"http://localhost:80/Chat");



I'm stumped again now. I can only think I'm not correctly creating an instance of the remoting object on the client, but I've no idea how to fix it. Could anyone enlighten me please? I'm at the phase of pulling my hair out... [Edited by - John H on July 22, 2006 8:26:49 AM]

Share this post


Link to post
Share on other sites
I think I've narrowed the problem down a bit, although I'm still uncertain as to what the cause is. I have a method within my remoting object, BroadcastMessage, which is called from the client. This method loops through all of the delegates subscribed to the remoting object's MessageArrived event, invoking each one as it goes, to send the message to each client in turn.

For debug purposes, I started to write the length of the GetInvocation() delegates array, to file. No matter how many clients I have, they are all reporting the length of the array as 1. Here's the full code for the method in question, including the logging:


public void BroadcastMessage(MessageArrivedEventArgs e)
{
MessageArrivedHandler msgHandler = null;

// Debug - this always reports 1!
StreamWriter sw = new StreamWriter("error.log", true);
sw.WriteLine(MessageArrived.GetInvocationList().Length.ToString());
sw.Close();
// End debug.

foreach (Delegate msgDelegate in MessageArrived.GetInvocationList())
{
try
{
msgHandler = (MessageArrivedHandler) msgDelegate;
// Just in case...
if (msgHandler != null)
msgHandler(this, e);
}
catch
{
// Client is no longer reachable, so remove it.
MessageArrived -= msgHandler;
}
}
}




Edit: I've also tried commenting out the try/catch statements to allow the code to run completely unguarded. It executes normally without throwing any exceptions.

So now I can't help but wonder why a client will only receive the message it sends, and won't broadcast messages/receive messages to/from other clients.

If I'm not providing enough information, let me know and thanks for your time as always.

Share this post


Link to post
Share on other sites
OK, one other thing then: how reliable is dumping code in the remoting object's constructor, in order to work out how many times it's being invoked? Let me explain.

I've just put the following code in the constructor of my remoting object, to see how many times it's being instantiated, with a view to making sure there is only 1 remoting object:


public class ChatRemoter : MarshalByRefObject
{
public ChatRemoter()
{
StreamWriter sw = new StreamWriter("error.log", true);
sw.WriteLine("Server constructed!");
sw.Close();
}

// Snip...
}



I was under the impression, that because this object is a server-activated singleton, that the constructor would only be fired once. However, when I'm trying to grab the reference of it, with:


// I've seen articles that say you can instantiate a
// server-activated singleton like this, only after
// you have registered it with the .config files.
remoteObj = new ChatRemoter();



That code calls that constructor once-per-client, so I end up with multiple entries of, "Server constructed!", in the log file. I was under the impression that after you use RemotingConfiguration.Configure();, using the new keyword would get a reference to the server object. I get the impression that isn't happening, and a new object is being created each time, which would explain why each client can send/receive messages only to itself but is unable to communicate with the other clients.

Can anyone confirm that to be true, and if so, how should I be getting the reference of my remoting object on the client? As I said earlier, I used to do it like this, when I was configuring the object programmatically, instead of via the config files:


// Get a reference to the remoting object and store it.
remoteObj = (ChatRemoter) Activator.GetObject(
typeof(ChatRemote.ChatRemoter),
"http://localhost:80/Chat");



That doesn't seem to work anymore, and not only that, it doesn't make sense to me why you would use that in this situation anyway. Surely, the whole point of configuring the remoting object with config files, is to eliminate the need to recompile your client/server, if you need to make a change. Supplying the URL parameter with Activator.GetObject() defeats that purpose.

Share this post


Link to post
Share on other sites
Mmmm... Remoting...

John, first of all, regarding this:

"I've also tried to use Activator.GetObject() but it throws an exception, saying, "File or assembly name, ChatRemoter could not be found." The confusing thing for me there, is I know the assembly isn't called ChatRemoter, it's called ChatRemote, so I don't know why it's looking for that."

The reason it is looking for assembly called ChatRemoter are these lines in your config files:

"<wellknown mode="Singleton" type="ChatRemoter, ChatRemoter" objectUri="Chat"/>"

"<wellknown type="ChatRemote.ChatRemoter, ChatRemoter" url="http://localhost:80/Chat"/>"

When you provide the type name for a well-known type, the first bit is the fully qualified type name, and the second is the assembly name. In your config files, the assembly name is ChatRemoter, not ChatRemote, that is why you are getting that error.

I suspect when you fix this, this may also resolve your client errors, because I guess when you instantiate the object this way, since you got the wrong assembly name in the config files, the constructor just resolves to the local ChatRemoter one. (Which, by the way, is why I personally prefer the activator approach - exceptions are good for you.)

To check this, consider your call:

remoteObj = new ChatRemoter();

Put a break-point on that line, and examine remoteObj with the watch or locals or autos window after it gets instantiated. I will bet you will see the insides of your chat remoter. If you do, that means the remoteObj is, in fact, instantiated locally, not remotely. If you see instead a type like "blahblahlblah._TransparentProxy" or some such, that will mean you got your remote object.

Hope that helps,
Vovan

Share this post


Link to post
Share on other sites
vovansim,

Thank you ever so much for your reply. Turns out that was the problem in terms of client communication. I'm not sure if you've seen my other thread, but basically, I switched over to using the .config file approach because when a client quits from the chat application, if I sent a message from one of the other active clients right away, it would seem to block for around 5 seconds. Someone I spoke to, send he had the same problem and that loading the objects from the .config files seemed to solve the problem. I tried more out of desperate really.

It's my own fault for having a crappy naming convention for that particular assembly. It's been bugging me for a while, and to be perfectly honest, I don't know why I didn't rename it earlier.

I can't thank you enough, though. That's been bugging me for way too long. Rate++.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this