Pass a remote object to a remote method in C# - c#

I'm attempting to pass a remote object as a parameter to a remote method, but I get a security exception when the remote object attempts to run a method on the received remote object.
This is a sample remote object:
public class SharpRemotingDLL : MarshalByRefObject
{
public String getRemoteMessage(SharpRemotingDLL server)
{
// the exception is raised here
return server.getMessage();
}
public String getMessage()
{
return "Hello World!";
}
}
This is the server starter (two instances of this are running, one on 127.0.0.10025, the other on 127.0.0.10026):
public static int Main(string[] args)
{
TcpServerChannel channel;
channel = new TcpServerChannel(10025);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(SharpRemotingDLL),
"SharpRemotingDLL",
WellKnownObjectMode.Singleton);
Console.ReadLine();
return 0;
}
And this is the client:
static void Main(string[] args)
{
SharpRemotingDLL server0 = (SharpRemotingDLL)
Activator.GetObject(typeof(SharpRemotingDLL),
"tcp://localhost:10025/SharpRemotingDLL");
SharpRemotingDLL servers[1] = (SharpRemotingDLL)
Activator.GetObject(typeof(SharpRemotingDLL),
"tcp://localhost:10026/SharpRemotingDLL");
Console.WriteLine(server0.getRemoteMessage(server1));
}
How do I correctly pass server1 as a parameter to the getRemoteMessage method?

vbigiani,
This article should shed some light on your problem:
http://weblogs.asp.net/jan/archive/2003/06/01/8106.aspx
If that doesn't work, you can get several relevant Google hits here:
Because of security restrictions, the type System.Runtime.Remoting.ObjRef cannot be accessed

I have written this kind of code before, but I have never tried to pass a server object to another server object in this way. I'm not a security expert, but I can sense why this might not be allowed.
You should obtain the message directly from Server1, rather than asking another remote server to return it. In other words, your message retrieval logic needs to be in the client.

Related

C# Pass data from While loop to other functions

I have a Node.Js server running on localhost listening to some API requests.
These requests are transferred to my console application via TCP/IP. Here's my c# code which receives data from Node server (hosted at localhost:9999) via GetData() and pass it to another function SendData().
namespace Datatransfer
{
/* global variable declaration*/
class Global
{
public static string receive_data;
}
class Program
{
static string HOST = "localhost";
static int PORT = 9999;
static TcpClient client;
/*Function to receive data*/
static string GetData()
{
while (true)
{
NetworkStream nwStream = client.GetStream();
byte[] bytesToRead = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
Global.receive_data= Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
Console.WriteLine("Received data : " + Global.receive_data);
SendData(Global.receive_data)
}
}
/*Function to send data*/
static void SendData(string val)
{
/*Code to process recevied_data..*/
Console.WriteLine("Data to Send : " + Global.receive_data);
/*some codes....*/
}
static void Main(string[] args)
{
client = new TcpClient();
client.Connect(HOST, PORT);
GetData();
}
}
}
I have declared the receive_data as global so as to use it across the application. The code works and I am getting output. Everytime I make an API request to port 9999 am getting output as :
Connection Successfull...
Received data : somestring
Data to Send : somestring
I was wondering if this is an efficient way or not ?
Is there another way by which the receive_data can be passed to other functions 'without' using the function ( ie;SendData() ) inside the while loop.? Or to put it simply, pass data from an infinite while loop to main or other functions.
Any suggestions?
You basically have two options for further processing the data you receive:
Store it somewhere like you did (from a design perspective it doesn't matter how you implement this). Just one thing to think about would be if you want to store a list of received data-"messages", and what happens if you receive another message.
Call a method an pass the received data. This would be the better approach, because you abstract away the implementation and are free to change it (e.g. from storing global to a message-sink mechanism or whatever) without changing your receiving-code.
Approach 2) has more information and more context, because you trigger the method at the point you receive data. In option 1) you have no information about how old the information is, or even if the same information was sent multiple times. So more information is (always) better, if you don't need it in the method call, you are free to condense it again to say a global variable.
For approach 2) you should keep in mind, that the method is running "inside" your loop, so all long-running operations would block the loop. But still you are free to implement it in a way, that allows the message to be processed in another thread (asynchronous).

Exception thrown when I pass the appropriate argument to method

I am reviewing source code of a voice chat application.
Here I want to run server program, so that any client can contact to server for voice chat. To run my server program I have to pass server name , port number and network interface that I am going to use for voice chat, after passing required arguments I have to call ServerStart method which is done by clicking on Start Checkbox in design view.If user has not passed appropriate type of arguments then it shows error by calling method ShowError().
Now, When I pass serverName, port number and Network Interface then serverName variable reference to null instead of the passed serverName argument.
Why An exception is thrown when I run server program that exception is "The source was not found, but some or all events logs could not be searched. Inaccessible logs: Security."
public partial class ServerWindow
{
private ChatServer server;
public delegate void SetListBoxItem(string str, string type);
public ServerWindow()
{
InitializeComponent();
ObtainNetworkInterfaces();
}
private void cbStartStop_Checked(object sender, RoutedEventArgs e)
{
if (cbStartStop.IsChecked == true)
{
// validate the port number
try
{
var port = Int32.Parse(tbPortNumber.Text);
server = new ChatServer(port, cbInterfaces.SelectedItem, tbServerName.Text);
server.ClientConnected += ServerOnClientConnected;
server.ClientDisconnected += ServerOnClientDisconnected;
var serverName = tbServerName.Text;
if (string.IsNullOrWhiteSpace(serverName))
{
ShowError();
}
else
{
server.StartServer();
SetControls(false);
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
{
if (server == null)
return;
server.StopServer();
SetControls(true);
}
}
private void ShowError()
{
MessageBox.Show(#"Please enter valid port number and/or server name");
cbStartStop.IsChecked = false;
}
The problem seems not to be in the code that you posted. After our chat I would suggest to look at this post try the accepted answer. I have the feeling it will solve your problem.
It is usually helpful to use the exception message in the catch clause. :)

.NET Remoting and Server Activated Objects

what's the problem with the following code...
I have this Complex class:
public class Complex : MarshalByRefObject
{
public double imaginary{get;set;}
public double real{get;set;}
public void setReal(double re)
{
real = re;
}
public void setImaginary(double im)
{
imaginary = im;
}
public Complex(double im, double re)
{
imaginary = im;
real = re;
}
public void writeMembers()
{
Console.WriteLine(real.ToString() + imaginary.ToString());
}
}
Actually, there's a little more to it, but the code it's too big, and we don't use the rest of it in the context of this.
Then, I implemented a server which listens for connections:
HttpChannel channel = new HttpChannel(12345);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(SharedLib.Complex), "ComplexURI", WellKnownObjectMode.SingleCall);
Console.WriteLine("Server started. Press any key to close...");
Console.ReadKey();
foreach (IChannel ichannel in ChannelServices.RegisteredChannels)
{
(ichannel as HttpChannel).StopListening(null);
ChannelServices.UnregisterChannel(ichannel);
}
Then, we have the client:
try
{
HttpChannel channel = new HttpChannel();
RemotingConfiguration.Configure("Client.exe.config", false);
Complex c1 = (Complex)Activator.GetObject(typeof(Complex), "http://localhost:12345/ComplexURI");
if (RemotingServices.IsTransparentProxy(c1))
{
c1.real = 4;
c1.imaginary = 5;
c1.writeMembers();
Console.ReadLine();
}
else
{
Console.WriteLine("The proxy is not transparent");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
Then, I run the server, which opens a console window, and I run the client.
Instead of displaying 4 and 5 on the server window, I merely get 00, a sign that the members weren't changed.
How do I do, so the members change?
Thanks.
The problem is that you're using WellKnownObjectMode.SingleCall. As the documentation says:
SingleCall Every incoming message is serviced by a new object instance.
Singleton Every incoming message is serviced by the same object instance.
See also the documentation for RegisterWellKnownServiceType:
When the call arrives at the server, the .NET Framework extracts the URI from the message, examines the remoting tables to locate the reference for the object that matches the URI, and then instantiates the object if necessary, forwarding the method call to the object. If the object is registered as SingleCall, it is destroyed after the method call is completed. A new instance of the object is created for each method called.
In your case, the statement c.Real = 4 is a call to the Real property setter. It makes a call to the remote object, which creates a new object, sets the Real property to 4, and returns. Then when you set the imaginary property, it creates a new object, etc.
If you want this to work, you'll have to use WellKnownObjectMode.Singleton. But you might want to ask yourself if you really want such a "chatty" interface. Every time you set a property, it requires a call through the proxy to the server.
And, finally, you might consider abandoning Remoting altogether. It's old technology, and has a number of shortcomings. If this is new development, you should be using Windows Communications Foundation (WCF). The Remoting documentation says:
This topic is specific to a legacy technology that is retained for backward compatibility with existing applications and is not recommended for new development. Distributed applications should now be developed using the Windows Communication Foundation (WCF).

Dynamically invoke a method over a socket

I have 2 pc's with some software, now I need one pc to execute a method on another pc, now I've searched high and low but could not find anything on how to do this. I could do this with writing my own little interface that serializes the arguments, method name and return objects and sending this over a socket then deserialize this execute the method using reflection, and return an result object over the socket. But I would like someone else's opinion before I start writing something that is much easier another way.
Send multiple arguments (they will all be received and send as an object)
Return an object
serialize back an exception object if any has occurred
I have not done anything in serializing objects and sending them over a socket, but are all standard objects serializable? Like a List<> array[] float dateTime?
I hope to have explained this ok, if not I'm sorry and ask what is not clear.
Create service WCF and config WCF to work over TCP.
This will give you most things 'out of the box' (serialize /deserialize, open/close socket )
There are good examples here, here and good reading here
I have searched the internet for examples and pasted together some code, i'm posting it here if somone needs it too. This is a dirty code but it works, the InvokeMethod is on the client side, and the startIBC is what needs to be started on each server:
[ServiceContract]
public interface IBlissRequest
{
[OperationContract]
object SystemRequest(string InstanceName, string MethodName, params object[] Parameters);
}
public class BlissRequest : IBlissRequest
{
public object SystemRequest(string InstanceName, string MethodName, params object[] Parameters)
{
return System21.BlissProcessingUnit.BPU.RequestFromIBC(InstanceName, MethodName, Parameters);
}
}
public static object InvokeMethod(string targetIpAddress, string InstanceName, string MethodName, params object[] Parameters)
{
try
{
var ep = "net.tcp://" + targetIpAddress + ":9985/IBC";
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
ChannelFactory<IBlissRequest> pipeFactory = new ChannelFactory<IBlissRequest>(binding, new EndpointAddress(ep));
IBlissRequest pipeProxy = pipeFactory.CreateChannel();
return pipeProxy.SystemRequest(InstanceName, MethodName, Parameters);
}
catch
{
BPUConsole.WriteLine(BPUConsole.WriteSource.IBC, "Unable to execute method: '" + MethodName +"' on Instance: '"+InstanceName+"' becouse IBC is unable to connect to: "+ targetIpAddress);
throw new Exception("Unable to connect to: " + targetIpAddress);
}
}
public static void StartIBC()
{
var uri = "net.tcp://" + BlissProcessingUnit.BPUInformation.LocalIpAddresses[0] + ":9985";
Console.WriteLine("Opening connection on: " + uri);
ServiceHost host = new ServiceHost(typeof(BlissRequest), new Uri[] { new Uri(uri) });
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
host.AddServiceEndpoint(typeof(IBlissRequest), binding, "IBC");
host.Open();
Console.WriteLine("Service is available. " + "Press <ENTER> to exit.");
}
What you are describing sounds like remote procedure calling (RPC). RPC allows you to create a single object on a server which clients can interact with as if it were a local object (so completely avoid the need to deal with sockets). Alternatively each client can also create its own unique server object to interact with.
A full implementation of RPC can be found in the network library networkcomms.net. The following code snippet is taken from the available RPC example and uses an object of type MathClass that can perform simple maths calculations.
The object exists server side:
//Register a single object server side called "Calculator"
RemoteProcedureCalls.Server.RegisterInstanceForPublicRemoteCall<MathClass, IMath>(new MathClass(), "Calculator");
On the client side:
//Get a reference to the remote object named "Calculator"
IMath calc = RemoteProcedureCalls.Client.CreateProxyToPublicNamedInstance<IMath>(connection, "Calculator", out instanceId);
//We can now use the calculator object as if it were local
//The following WriteLine outputs '12' where the calculation was performed on the server
Console.WriteLine(calc.Multiply(4, 3));
Disclaimer: I have to add that I am a developer for this library.

Unable to read data from the transport connection: the connection was closed

The exception is Remoting Exception - Authentication Failure. The detailed message says "Unable to read data from the transport connection: the connection was closed."
I'm having trouble with creating two simple servers that can comunicate as remote objects in C#. ServerInfo is just a class I created that holds the IP and Port and can give back the address. It works fine, as I used it before, and I've debugged it. Also the server is starting just fine, no exception is thrown, and the channel is registered without problems. I'm using Forms to do the interfaces, and call some of the methods on the server, but didn't find any problems in passing the parameters from the FormsApplication to the server when debugging. All seems fine in that chapter.
public ChordServerProgram()
{
RemotingServices.Marshal(this, "PADIBook");
nodeInt = 0;
}
public void startServer()
{
try
{
serverChannel = new TcpChannel(serverInfo.Port);
ChannelServices.RegisterChannel(serverChannel, true);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
I run two instances of this program. Then startNode is called on one of the instances of the application. The port is fine, the address generated is fine as well. As you can see, I'm using the IP for localhost, since this server is just for testing purposes.
public void startNode(String portStr)
{
IPAddress address = IPAddress.Parse("127.0.0.1");
Int32 port = Int32.Parse(portStr);
serverInfo = new ServerInfo(address, port);
startServer();
//node = new ChordNode(serverInfo,this);
}
Then, in the other istance, through the interface again, I call another startNode method, giving it a seed server to get information from. This is where it goes wrong. When it calls the method on the seedServer proxy it just got, a RemotingException is thrown, due to an authentication failure. (The parameter I'll want to get is the node, I'm just using the int to make sure the ChordNode class has nothing to do with this error.)
public void startNode(String portStr, String seedStr)
{
IPAddress address = IPAddress.Parse("127.0.0.1");
Int32 port = Int32.Parse(portStr);
serverInfo = new ServerInfo(address, port);
IPAddress addressSeed = IPAddress.Parse("127.0.0.1");
Int32 portSeed = Int32.Parse(seedStr);
ServerInfo seedInfo = new ServerInfo(addressSeed, portSeed);
startServer();
ChordServerProgram seedServer = (ChordServerProgram)Activator.GetObject(typeof(ChordServerProgram), seedInfo.GetFullAddress());
// node = new ChordNode(serverInfo,this);
int seedNode = seedServer.nodeInt;
// node.chordJoin(seedNode.self);
}
Try setting the ensureSecurity to false, and it should start working.
ChannelServices.RegisterChannel(serverChannel, false);
You've specified that security is a must on your Remoting server in startServer() with:
ChannelServices.RegisterChannel(serverChannel, true);
Yet the 'client' end does not specify security, hence the authorisation error. You need to specify tcp channel security on both ends unless the server security setting is set to 'false'. In your second startNode method you need to do the following before using Activator.GetObject, note no port specified on the TcpChannel unlike the server end:
TcpChannel ClientChan = new TcpChannel();
ChannelServices.RegisterChannel(ClientChan, true);
Furthermore, unless you're doing it in some code you haven't given us, you also do not seem to have registered a well known service type server side, although you say it's been working in the debugger so maybe that's not necessary in the case. See MSDN on RegisterWellKnownServiceType.

Categories