|
I'm not sure then, I'd take a thorough look through your drawing code since it is more likely to be there (but it's not impossible that it's a CLR bug).
Formula 1 - Short for "F1 Racing" - named after the standard "help" key in Windows, it's a sport where participants desperately search through software help files trying to find actual documentation. It's tedious and somewhat cruel, most matches ending in a draw as no participant is able to find anything helpful. - Shog9
Ed
|
|
|
|
|
I can't count how many .NET remoting tutorials and book excerpts i read. Every single time the example involves a chat client passing strings or some other example where strings, ints, and simple user defined types are begin passed. I am looking for an example which shows me how to pass pointer types, like most things in .NET library. For example, I want to pass a FileInfo or FileStream object from server to client. I tried that, it didn't work. Someone told me that's because System.IO.FileStream object, for example, is a pointer to a memory location which would have meaning on the machine it was instantiated on. If I pass that pointer to another machine than that pointer would have no meaning on that machine.
But I did check and FileInfo object derives from MarshalByRefObject, so I thought that means it could be used accross machine boundries.
I am trying to write the simplest and non-dirties of file transfer apps where the client passes a FileStream to a server, server reads it and saves it localy. Simple stuff. Cannot figure it out.
Please help.
|
|
|
|
|
You have to consider what you're asking the machine to do. You want to pass a file stream from one machine to another. Are you sending all the data in that stream or just a pointer to the stream? Assuming you send a pointer, is the intention for you to read a stream from another computer? It's just not going to work that way. You can either send over the entire file or you can open up a tcp channel and stream it through there.
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
Yes, the intention is for server to get a pointer to a FileStream from a client machine then save it on the server. To make things simple, lets say the server needs to read 4 bytes from clients file:
Client:
-------
FileStream fileStream = new FileStram("C:\\Some File.txt");
// Assume all the .NET remoting connection stuff was already established
Server server = new Server();
server.Send(fileStream);
Server:
----------
void Send(FileStream fileStream)
{
byte[] someData = new byte[4];
fileStream.Read(someData, 0, someData.Length);
// then do something with that data
}
That does not work for me, the app will hang. But I do not see why. FileStream is a pointer yes, but it is a type that derives from MarshalByRefObject. So when the server executes "fileStream.Read(someData, 0, someData.Length);" it has to make a call to the client which will execute that method on the FileStream object, then return the read bytes back to server. I thought thats the whole point of .NET remoting, the ability to call objects across machine boundries.
|
|
|
|
|
Well, yes, FileStream is a MarshalByRefObject . But, internally, it has a cursor that is inside the client's file. That cursor is completely invalid on any other machine except the client. Trying to read data from a client machine through the server will just not work. There are only two ways to accomplish this: send the entire file to the server or open up a network channel on the client and allow the server to read the data through that channel.
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
See, I'm trying to understand .NET Remoting, and if object were people here is how I think they would talk (based on the code sample in my previous response).
Client To Server: "Server, Im sending u a FileStream object. If you want to know anything about it, let me know. From your point of you, this is just a description of the object. You can't do anything with it. Im just letting you know that I have this object and what features this object has. Ask me and I will tell you anything about it.
Server To Client: "Ok, I want you to read 4 bytes from the object starting at the begining and then send the bytes back to me."
Client To Server: [CLIENT Reads the 4 bytes locally] "Ok, there you go, I read the 4 bytes from the beginning and sent it to you."
So I thought thats the idea with MarshalByRefObjects as opposed to [Serializable] objects. If the cursor is invalid on any other machine then that shouldnt matter. Because the server machine is marshaling all calls to the client, the server is not actually doing anything with the object. When a certain function is executed on the object on the server, the server asks two questions "What do I need to pass to the client and what will the client return to me?" Then it marhals a call to the client.
I the case of "fileStream.Read(someData, 0, someData.Length);" the server doesnt do jack with filesSream, it just lets the client know that the it should read 4 bytes from the file and send it back to the server. Then the sever would store the data in "someData" array. If this is not the case then I am totaly lost.
-- modified at 14:51 Monday 31st July, 2006
|
|
|
|
|
Well, let's clear up the terminology a little bit. MarshalByRefObject indicates to remoting that a proxy has to be created. So, when you send a FileStream to the server, you're actually creating a proxy on the server side that it can pass it's calls through to the client. Calling it a pointer is what was confusing me because it sounded like you wanted to transfer the serialized FileStream object over to the server.
Anyways, you can use the FileStream on the server side, because .Net remoting will create the proxy for you. But this is very inefficient. The call to the proxy will be interpreted and sent to the client, which will do the work then interpret and send it back to the server, which has to be interpreted again and send back to the caller. It's much faster to open a channel and just send the file.
Also, I understand you have a client application and a server application and the file needs to go from client to server. But, in remoting, the side that has the proxy is the client. Your client application will actually have to become a server. This is my understanding of how it works anyway. The server appliation needs a proxy and the only way it can get that is by being a client.
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
I don't know why it is failing for you. I've done the same thing with a memory stream between appDomains, which should perform the same way, but it might not. It probably makes more sense to create your own marshalByRef proxy object wrapping the filestream to pass in as a parameter. That will give you more freedom to change the internal implementation in the future and you should be able to debug the methods of your custom object.
Also, if you are running these from seperate systems disable any firewalls that might be causing problems allowing the return connection from the server back to the client.
-- modified at 16:22 Monday 31st July, 2006
I can imagine the sinking feeling one would have after ordering my book,
only to find a laughably ridiculous theory with demented logic once the book arrives - Mark McCutcheon
|
|
|
|
|
I somehow think it has something to do with secuirty. I am going to dig into it a bit, it's just frustrates me how little there is on .net remoting there is. Even books like "Advanced .NET Remoting" would start off with chat client sample and then divulge into writing your own sinks and lease times and all that, not single one tutorial used something like passing a Form or a Button from client to server, its all strings and ints and bytes.
|
|
|
|
|
A typical, safe deployment of remoting is to keep valuable resources local and make value data passable. Now in your example you wanted to have something remote and get bits of data from the file. To actually do this there are one of two ways that you can safely do it:
1) make successive remote calls to a server side component. The server side component manages what file it is working with, where in the file the last process occured, and what needs to be returned next. The data itself would marshal across but the rare resource (file handle) remains on the server.
However...the above scenario will only work if you have very long lags between each request and you can maintain. If, on the other hand, you need to frequently interogate the data then you do a client/server deployment as follows:
2) Server side gets the entire file and marshals it across to a client interface. The client interface is the go-between between your code and the server. It's job is to maintain the data it gets from the server (determines if it needs to get new data or not), as well as all of the tasks listed in 1).
Now in the second scenario you are thread-safe, have a handle on the data, and make your remoting call 'chunky'. Keep in mind that chunky calls is the difference between having a performing application and having half-hour response time for a user process.
|
|
|
|
|
Thanks for all your help so far. Conside the following C# 2.0 simple client-server code:
Client:
--------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.IO;
namespace RemotingTest
{
class Program
{
static void Main(string[] args)
{
IDictionary properties = new Hashtable();
SoapClientFormatterSinkProvider clientSinkProvider = new SoapClientFormatterSinkProvider();
SoapServerFormatterSinkProvider serverSinkProvider = new SoapServerFormatterSinkProvider();
serverSinkProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
properties["name"] = "";
properties["port"] = 0;
properties["typeFilterLevel"] = "Full";
HttpChannel httpChannel = new HttpChannel(properties, clientSinkProvider, serverSinkProvider);
ChannelServices.RegisterChannel(httpChannel, false);
RemotingConfiguration.RegisterWellKnownClientType(
typeof(ServerInterface),
"http://igor.no-ip.ca:1024/Server"); // change to appropriate address here
ServerInterface server = new ServerInterface();
string text = "";
while((text = Console.ReadLine()) != "Quit")
{
Console.WriteLine(server.ShowText("testing string"));
Console.WriteLine(server.ShowFileInfo(text).Name);
}
Console.Read();
}
}
}
Server Interface:
------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace RemotingTest
{
public class ServerInterface: MarshalByRefObject
{
public FileInfo ShowFileInfo(string filename)
{
Console.WriteLine("Filename " + filename + " requested");
return new FileInfo(filename);
}
public string ShowText(string text)
{
Console.WriteLine(text);
return "\"" + text + "\" show on the server.";
}
}
}
Server:
-------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
namespace RemotingTest
{
class Program
{
static void Main(string[] args)
{
IDictionary properties = new Hashtable();
SoapServerFormatterSinkProvider serverSinkProvider = new SoapServerFormatterSinkProvider();
serverSinkProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
properties["name"] = "";
properties["port"] = 1024;
properties["typeFilterLevel"] = "Full";
HttpChannel httpChannel = new HttpChannel(properties, null, serverSinkProvider);
ChannelServices.RegisterChannel(httpChannel, false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ServerInterface),
"Server", WellKnownObjectMode.SingleCall);
Console.Read();
}
}
}
-------------------------------------------------------------------------
The above sample would work no problem if you launch the server and client on the same machine. However, move them across machine boundries and you will find that passing a string and getting a response from server.ShowText() works, but the client will give you an exception after 30 sec. or so trying to execute server.ShowFileInfo();
To test the programs, enter properly formatted filenames, like "C:\test.txt" or something in the client and if you want to quit, type in "Quit". The filename does not have to exist, it just has to have proper format.
For you convinience, i zipped up VS2005 solution here: http://igor.no-ip.ca/remoting%20test.zip
Thanks!
|
|
|
|
|
Am I missing something here? Why is your client not calling Activator.CreateInstance or Activator.GetObject?
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
Beacause I called RegisterWellKnownClientType() on the client. That allows me to instantiate objects using "new". MSDN: http://msdn2.microsoft.com/en-us/library/hcd5d6y7.aspx
|
|
|
|
|
Ah ok. So what happens if you have your file on both machines in the same place?
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
You mean the file that I pass to server.ShowFileInfo() method? If I have a file named text.txt on drive C:\ on both server and client the proram would still fail.
|
|
|
|
|
Just eliminating possibilities. Your code works fine on two different machines. Normally I'd say it's a security problem, but if you're logged into both machines running a console app, then that's not really a concern.
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
Hmm, I did test this code across my home and work computers. Maybe I should try other combinations. What I do is I have the client running on the machine that I am using, and the server on the machine that I use remote desktop to connect to. It could also be a prob. with firewall. On the server machine that I use Remote Desktop to connect to I have port 1024 open. Maybe when the whole marshalling process takes place other ports need to be open that I dont know about?
|
|
|
|
|
Ya, it does sound odd. You're able to get past the first statement, so there's definitely a connection. Since you're remote desktop'd into the work server, your server app is running under whatever windows user you have, so that's not a problem as long as you can access that file. Can you try a TcpChannel instead to see if that has any different results? There might be firewall issues there somewhere. Maybe you can grant that server application access through the firewall?
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
I changed the channels to Tcp and both server and client are setup with Windows Firewall. That didnt help. I am going to try this code on a local network where a router will not be invlolved and I will turn off firewalls.
|
|
|
|
|
I found the problem !!! The programs do work on separate computers over a local network. Firewalls were turned off, so they are not the problem. Port forwarding on the routers must be the problem. Why the ShowText() method works and ShowFileInfo() fails is a mystery. Maybe there are more ports open then one would think. ShowFileInfo() does return a reference to an object, so maybe there is some sort of 2-way communictaion happening that requires additional ports.
-- modified at 15:44 Thursday 3rd August, 2006
|
|
|
|
|
Mk, after further examintation and tracing using Wireshack (formerly Ethereal) here is what I found.
Firs the setup.
Home computer which has the server:
address: igor.no-ip.ca
local address of nic: 10.0.0.1
Work computer which has the client:
address: pennylane.no-ip.ca
local nic address: 192.168.0.101
Both computers are behind routers with port 1024 open and forwarded.
Basicaly, looking at the soap messages, it looks like when ShowText() method is called, the soap message is relatively small. When ShowFileInfo() method is called, the soap message is big, and one of the entries in that message is address 10.0.0.1, which is the local address of the server, and to the client has no meaning. But, after receiving that message, the client tries to connect to 10.0.0.1 on port 1024!
Here is the trace:
---------------------------------------------------------------------------------------
http://img394.imageshack.us/img394/3109/remotingtestyk0.png
-------------------------------------------------------------------------------
If you look at server's soap response message (the last one) you will see that item id="ref-17" is set to http://10.0.0.1:1024. The client must be reading this value because the next thing it does is it tries to connect to 10.0.0.1:1024, which, ofcourse, fails.
Here are complete trace files in case you are interested: http://igor.no-ip.ca/remoting%20trace%20test.zip (tcpdump / ethereal format)
VS2005 Solution: http://igor.no-ip.ca/remoting%20test.zip
Any ideas?
-- modified at 14:23 Friday 4th August, 2006
|
|
|
|
|
|
Thanks for the quick reply. I actually figured out the problem. May not be a problem after all, just the way .NET developers designed the library. There is a peculiar channel property called "machineName" which by default you shouln't have to worry about: http://msdn2.microsoft.com/en-us/library/kw7c6kwc(d=ide).aspx#machineName
Setting the property on the server to the current "outside" address solves the problem !!! Here is what I think happens:
When ShowText() is called, nothing special takes place b/c the client knows what to pass and what to get back. Done deal. With ShowFileInfo(), the FileInfo object can incurr additional calls from the client side. That is why in that case the server will return a message with a bunch of stuff and one of them being the server's ip address. Dont know whats the need for that, since the client already knows the server's ip address. But the server is "dumb" enough to put its local address in message, not the wan address. So setting "machineName" to the wan address of the server does the trick. Here is the soap message returned by the server to the client upon ShowFileInfo():
http://img185.imageshack.us/img185/1772/remotingtestworksfu1.png
Note how item id="ref-17 is now set to "igor.no-ip.ca:1024" as opposed to "10.0.0.1:1024". It makes sence now.
So here, in all glory is a working code:
----------------------------------------
Client:
<br />
using System;<br />
using System.Collections;<br />
using System.Collections.Generic;<br />
using System.Text;<br />
using System.Runtime.Remoting;<br />
using System.Runtime.Remoting.Channels;<br />
using System.Runtime.Remoting.Channels.Http;<br />
using System.IO;<br />
<br />
namespace RemotingTest<br />
{<br />
class Program<br />
{<br />
static void Main(string[] args)<br />
{<br />
IDictionary properties = new Hashtable();<br />
<br />
SoapClientFormatterSinkProvider clientSinkProvider = new SoapClientFormatterSinkProvider();<br />
SoapServerFormatterSinkProvider serverSinkProvider = new SoapServerFormatterSinkProvider();<br />
<br />
serverSinkProvider.TypeFilterLevel =<br />
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;<br />
<br />
properties["name"] = "";<br />
properties["port"] = 0;<br />
properties["typeFilterLevel"] = "Full";<br />
<br />
HttpChannel commChannel = new HttpChannel(properties, clientSinkProvider, serverSinkProvider);<br />
<br />
ChannelServices.RegisterChannel(commChannel, false);<br />
<br />
RemotingConfiguration.RegisterWellKnownClientType(<br />
typeof(ServerInterface),<br />
"http://igor.no-ip.ca:1024/Server");
<br />
ServerInterface server = new ServerInterface();<br />
<br />
string text = "";<br />
<br />
while((text = Console.ReadLine()) != "Quit")<br />
{<br />
Console.WriteLine(server.ShowText("testing string"));<br />
Console.WriteLine(server.ShowFileInfo(text).Name);<br />
}<br />
<br />
Console.Read();<br />
}<br />
}<br />
}<br />
Server Interface:
-----------------------
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Text;<br />
using System.IO;<br />
<br />
namespace RemotingTest<br />
{<br />
public class ServerInterface: MarshalByRefObject <br />
{<br />
public FileInfo ShowFileInfo(string filename)<br />
{<br />
Console.WriteLine("Filename " + filename + " requested");<br />
<br />
return new FileInfo(filename);<br />
}<br />
<br />
public string ShowText(string text)<br />
{<br />
Console.WriteLine(text);<br />
<br />
return "\"" + text + "\" show on the server.";<br />
}<br />
}<br />
}<br />
Server:
-------------------
<br />
using System;<br />
using System.Collections;<br />
using System.Collections.Generic;<br />
using System.Text;<br />
using System.Runtime.Remoting;<br />
using System.Runtime.Remoting.Channels;<br />
using System.Runtime.Remoting.Channels.Http;<br />
<br />
namespace RemotingTest<br />
{<br />
class Program<br />
{<br />
static void Main(string[] args)<br />
{<br />
IDictionary properties = new Hashtable();<br />
<br />
SoapServerFormatterSinkProvider serverSinkProvider = new SoapServerFormatterSinkProvider();<br />
<br />
serverSinkProvider.TypeFilterLevel =<br />
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;<br />
<br />
properties["name"] = "";<br />
properties["port"] = 1024;<br />
properties["typeFilterLevel"] = "Full";<br />
properties["machineName"] = "igor.no-ip.ca";
<br />
HttpChannel commChannel = new HttpChannel(properties, null, serverSinkProvider);<br />
<br />
ChannelServices.RegisterChannel(commChannel, false);<br />
<br />
RemotingConfiguration.RegisterWellKnownServiceType(<br />
typeof(ServerInterface),<br />
"Server", WellKnownObjectMode.SingleCall);<br />
<br />
Console.Read();<br />
}<br />
}<br />
}<br />
----------------------
Now its time for me to write an article on this. Having a router is quite common, and Im sure other programmers will stumble on the same problem.
Thanks for all you help guys!!
-- modified at 15:31 Friday 4th August, 2006
|
|
|
|
|
Okay, that's great! You still have a broken network and I would highly suggest you fix it before you go much further and I mean that seriously. Please don't be advising others on how something may work until you've tested it thoroughly on a functioning network.
- Rex
|
|
|
|
|
I did follow your guide and changed everything to the suggested settings. It was very detailed, thank you. It did not solve my problem though, but I am sure it will prevent me from having other network-related problems.
-- modified at 9:18 Saturday 5th August, 2006
|
|
|
|
|