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).
Related
For practicing I wanted to create client and server applications to simulate a lobby.
Therefore, in the server-application I accept incoming connections, create a ClientInfo object containing the TcpClient object, usernames, id, etc. and the methods for sending and receiving data, and store that ClientInfo object in a List in my lobby class. When the user does something like chatting, the message is being sent to the server and broadcasted to all available clients.
The problem I have is:
The first client connects. Broadcasts go to DefaultUser1.
The second client connects. Broadcasts go to DefaultUser2 + DefaultUser2.
As you can see, the first Client is not receiving data anymore, nor can the Server receive data from him. Somehow the data in the list must be corrupted. Here is the relevant bit of code:
Accepting incoming conenctions and creating the ClientInfo object and storing it to the lobby:
while (mWorking)
{
TcpClient client = mListener.AcceptTcpClient();
mNumberOfClients++;
Console.WriteLine("New Tcp-Connection with client: " + client.Client.LocalEndPoint.ToString());
ClientInfo newInfo = new ClientInfo(client, mNumberOfClients);
mLobby.AddClient(newInfo);
}
The ClientInfo constructor:
public ClientInfo(TcpClient client, int clientNumber)
{
mClient = client;
mClientNumber = clientNumber;
mUsername = "DefaultUser" + mClientNumber.ToString();
mStream = client.GetStream();
mEncoding = new ASCIIEncoding();
}
The sending method in ClientInfo:
public void Send(String message)
{
mCurrentMessage = message;
Thread sendThread = new Thread(this.WriteTask);
sendThread.Start();
}
private void WriteTask()
{
byte[] data = mEncoding.GetBytes(mCurrentMessage);
byte[] sizeinfo = new byte[4];
sizeinfo[0] = (byte)data.Length;
sizeinfo[1] = (byte)(data.Length >> 8);
sizeinfo[2] = (byte)(data.Length >> 16);
sizeinfo[3] = (byte)(data.Length >> 24);
mStream.Write(sizeinfo, 0, sizeinfo.Length);
mStream.Write(data, 0, data.Length);
}
Relevant code in the lobby class:
private static List<ClientInfo> mClients;
private static processDel mProcessDel;
public Lobby(processDel del)
{
mProcessDel = del;
mClients = new List<ClientInfo>();
}
public void AddClient(ClientInfo client)
{
mClients.Add(client);
client.Listen(mProcessDel);
Broadcast("UJOIN§" + client.username + "$");
}
public void Broadcast(String message)
{
for (int i = 0; i < mClients.Count; i++)
{
Console.WriteLine("Broadcasting to " + mClients[i].username);
mClients[i].Send(message);
}
}
I also tried the broadcasting with foreach, same result. The processDel is a delegate method i need for processing the received data. Receiving is handled by a seperate thread for each client.
As a guess, it seems that you misunderstood what static means in C#.
static means that the method or field is part of the type, rather than the instance of a type. So if all of your fields are static, you don't actually have any instance data, and all the state is shared across all instances of your class - so the second client overwrites all the data associated with the first client as well. The solution is simple - just remove the statics, and you should be fine.
Other than that, your code has some thread-safety issues. Most types in .NET are not thread-safe by default, and you need to add appropriate locking to make sure that consistency is maintained. This is more of a topic for CodeReview, perhaps, so I'll just note the first things that come to mind:
Send always starts a new thread to send the message. However, this also means that if it's called twice in succession under just the right conditions, it can completely corrupt your TCP stream - for example, the first thread might write the length data, then the second writes its length data before the first writes the actual data and you're in trouble. It's also possible that you'd just send the second message twice, since you're passing the text to send through a field.
List<T> isn't thread-safe. That means that you can only safely use it from a single thread - it's not entirely clear from your code, but it seems like you might have trouble with that. Using something like ConcurrentDictionary<IPEndPoint, ClientInfo> might be a better idea, but that really depends on what you're doing.
You could also explore some alternative options, like using asynchronous I/O instead of spamming threads, but that's a bit more advanced option (mind you, multi-threading is even worse :)). Regardless, a good start for thread-safety would be http://www.albahari.com/threading/ It's somewhat long, but multi-threading is a very complex and dangerous topic, and it will tend to produce errors that are hard to find and reproduce, especially while running in a debugger.
I have two self hosted services running on the same network. The first is sampling an excel sheet (or other sources, but for the moment this is the one I'm using to test) and sending updates to a subscribed client.
The second connects as a client to instances of the first client, optionally evaluates some formula on these inputs and the broadcasts the originals or the results as updates to a subscribed client in the same manner as the first. All of this is happening over a tcp binding.
My problem is occuring when the second service attempts to subscribe to two of the first service's feeds at once, as it would do if a new calculation is using two or more for the first time. I keep getting TimeoutExceptions which appear to be occuring when the second feed is subscribed to. I put a breakpoint in the called method on the first server and stepping through it, it is able to fully complete and return true back up the call stack, which indicates that the problem might be some annoying intricacy of WCF
The first service is running on port 8081 and this is the method that gets called:
public virtual bool Subscribe(int fid)
{
try
{
if (fid > -1 && _fieldNames.LeftContains(fid))
{
String sessionID = OperationContext.Current.SessionId;
Action<Object, IUpdate> toSub = MakeSend(OperationContext.Current.GetCallbackChannel<ISubClient>(), sessionID);//Make a callback to the client's callback method to send the updates
if (!_callbackList.ContainsKey(fid))
_callbackList.Add(fid, new Dictionary<String, Action<Object, IUpdate>>());
_callbackList[fid][sessionID] = toSub;//add the callback method to the list of callback methods to call when this feed is updated
String field = GetItem(fid);//get the current stored value of that field
CheckChanged(fid, field);//add or update field, usually returns a bool if the value has changed but also updates the last value reference, used here to ensure there is a value to send
FireOne(toSub, this, MakeUpdate(fid, field));//sends an update so the subscribing service will have a first value
return true;
}
return false;
}
catch (Exception e)
{
Log(e);//report any errors before returning a failure
return false;
}
}
The second service is running on port 8082 and is failing in this method:
public int AddCalculation(string name, string input)
{
try
{
Calculation calc;
try
{
calc = new Calculation(_fieldNames, input, name);//Perform slow creation before locking - better wasted one thread than several blocked ones
}
catch (FormatException e)
{
throw Fault.MakeCalculationFault(e.Message);
}
lock (_calculations)
{
int id = nextID();
foreach (int fid in calc.Dependencies)
{
if (!_calculations.ContainsKey(fid))
{
lock (_fieldTracker)
{
DataRow row = _fieldTracker.Rows.Find(fid);
int uses = (int)(row[Uses]) + 1;//update uses of that feed
try
{
if (uses == 1){//if this is the first use of this field
SubServiceClient service = _services[(int)row[ServiceID]];//get the stored connection (as client) to that service
service.Subscribe((int)row[ServiceField]);//Failing here, but only on second call and not if subscribed to each seperately
}
}
catch (TimeoutException e)
{
Log(e);
throw Fault.MakeOperationFault(FaultType.NoItemFound, "Service could not be found");//can't be caught, if this timed out then outer connection timed out
}
_fieldTracker.Rows.Find(fid)[Uses] = uses;
}
}
}
return id;
}
}
catch (FormatException f)
{
Log(f.Message);
throw Fault.MakeOperationFault(FaultType.InvalidInput, f.Message);
}
}
The ports these are on could change but are never shared. The tcp binding used is set up in code with these settings:
_tcpbinding = new NetTcpBinding();
_tcpbinding.PortSharingEnabled = false;
_tcpbinding.Security.Mode = SecurityMode.None;
This is in a common library to ensure they both have the same set up, which is also a reason why it is declared in code.
I have already tried altering the Service Throttling Behavior for more concurrent calls but that didn't work. It's commented out for now since it didn't work but for reference here's what I tried:
ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 400,
MaxConcurrentSessions = 400,
MaxConcurrentInstances = 400
};
host.Description.Behaviors.RemoveAll<ServiceThrottlingBehavior>();
host.Description.Behaviors.Add(stb);
Has anyone had similar issues of methods working correctly but still timing out when sending back to the caller?
This was a difficult problem and from everything I could tell, it is an intricacy of WCF. It cannot handle one connection being reused very quickly in a loop.
It seems to lock up the socket connection, though trying to add GC.Collect() didn't free up whatever resources it was contesting.
In the end the only way I found to work was to create another connection to the same endpoint for each concurrent request and perform them on separate threads. Might not be the cleanest way but it was all that worked.
Something that might come in handy is that I used the svc trace viewer to monitor the WCF calls to try and track the problem, I found out how to use it from this article: http://www.codeproject.com/Articles/17258/Debugging-WCF-Apps
I'm new in WCF, just learning it so far. But I was wondering how to close a WCF client connection from the client side (if it is needed at all? I think so).
I have an interface called
[ServiceContract]
ICalculatorService { blabla... }
The question is on the client side.
So far, I used the following format:
EndpointAddress epAddress = new EndpointAddress("http://localhost:8090/CalculatorService");
ICalculatorService calculatorProxy = ChannelFactory<ICalculatorService>.CreateChannel(new WSHttpBinding(), epAddress);
and now I can:
Result numeralResult = calculatorProxy.AddNumbers(4, 5);
and I got the result and I was happy.
Every single (for example) Button pressing caused the mentioned code to run once.
My question is: is the efficient?
Now I'm thinking of putting this into a separate class, for example:
class CalculatorProxy
{
static EndpointAddress epAddress = new EndpointAddress("http://localhost:8090/CalculatorService");
public static ChannelFactory<ICalculatorService> GetCalculatorProxy()
{
}
public void Dispose() { ... }
}
... and use it like:
using (ICalculatorService calculatorClient = CalculatorProxy.GetCalculatorProxy())
{
calculatorClient.AddNumbers(4, 4);
}
which one would be more efficient?
UPDATE:
Thank you all for your answers.
I finally ended up with this class:
class CalculatorServiceClient : ClientBase<ICalculatorService>, IDisposable
{
static EndpointAddress epAddress = new EndpointAddress("http://localhost:8090/CalculatorService");
ICalculatorService myCalculatorProxy;
public CalculatorServiceClient()
: base(new WSHttpBinding(), epAddress)
{
myCalculatorProxy = ChannelFactory.CreateChannel();
}
public static CalculatorServiceClient GetNewInstance()
{
return new CalculatorServiceClient();
}
public Result AddNumbers(int aIn, int bIn)
{
return myCalculatorProxy.AddNumbers(aIn, bIn);
}
public void Dispose()
{
try
{
Close();
}
catch (CommunicationObjectFaultedException ex)
{
throw new DBCommunicationException("CalculatorServiceClient is in the Faulted state.", ex);
}
catch (Exception ex)
{
throw new DBCommunicationException("Communication is unsuccessful between the CalculatorServiceClient and the CalculatorService.", ex);
}
}
}
And use it in this way:
try
{
using (CalculatorServiceClient calculatorClient = CalculatorServiceClient.GetNewInstance())
{
Result aResult = calculatorClient.AddNUmbers(tbA.Text, tbB.Text);
}
}
catch (DBCommunicationException ex)
{
MessageBox.Show("Service is shut down.");
}
My question is: is this efficient?
You should just close the client when each operation you have done is completed and you don't need anymore to make other calls.
When your work is finished, just close the client using the Close method:
calculatorProxy.Close();
About the Close() method, the MSDN documentation states:
This method causes a CommunicationObject to gracefully transition from
any state, other than the Closed state, into the Closed state. The
Close method allows any unfinished work to be completed before
returning. For example, finish sending any buffered messages.
About your approach, I think the second one is fine and more efficient, because you're also implementing the Dispose pattern and release the used resources (this depends on the resources you're using). Just add the Close() method when the work is finished:
calculatorClient.AddNumbers(4, 4);
calculatorProxy.Close();
Remember also that there's no performance issue creating and closing continuously the WCF clients. This is just a normal habitude.
using (var client = new CalculatorServiceClient())
{
client.SomeMethod();
}
The CalculatorServiceClient object will be available once you add a Service Reference to your Calculator WebService to your client project.
you can call the close method of your proxy class.
like
calculatorProxy.Close();
Alternatively you call the abort method on your service proxy class in case of exception.
try
{
calculatorProxy.SomeMethod();
calculatorProxy.Close();
}
catch
{
calculatorProxy.Abort();
}
Refer to this link for further details
I think you would find it better to put all of that in a class. Establishing an instance of the class can construct the connection and close/dispose when the time comes. Until then, you have an open and active channel to make calls to.
I'm having an issue with ZeroMQ, which I believe is because I'm not very familiar with it.
I'm trying to build a very simple service where multiple clients connect to a server and sends a query. The server responds to this query.
When I use REQ-REP socket combination (client using REQ, server binding to a REP socket) I'm able to get close to 60,000 messages per second at server side (when client and server are on the same machine). When distributed across machines, each new instance of client on a different machine linearly increases the messages per second at the server and easily reaches 40,000+ with enough client instances.
Now REP socket is blocking, so I followed ZeroMQ guide and used the rrbroker pattern (http://zguide.zeromq.org/cs:rrbroker):
REQ (client) <----> [server ROUTER -- DEALER --- REP (workers running on different threads)]
However, this completely screws up the performance. I'm getting only around 4000 messages per second at the server when running across machines. Not only that, each new client started on a different machine reduces the throughput of every other client.
I'm pretty sure I'm doing something stupid. I'm wondering if ZeroMQ experts here can point out any obvious mistakes. Thanks!
Edit: Adding code as per advice. I'm using the clrzmq nuget package (https://www.nuget.org/packages/clrzmq-x64/)
Here's the client code. A timer counts how many responses are received every second.
for (int i = 0; i < numTasks; i++) { Task.Factory.StartNew(() => Client(), TaskCreationOptions.LongRunning); }
void Client()
{
using (var ctx = new Context())
{
Socket socket = ctx.Socket(SocketType.REQ);
socket.Connect("tcp://192.168.1.10:1234");
while (true)
{
socket.Send("ping", Encoding.Unicode);
string res = socket.Recv(Encoding.Unicode);
}
}
}
Server - case 1: The server keeps track of how many requests are received per second
using (var zmqContext = new Context())
{
Socket socket = zmqContext.Socket(SocketType.REP);
socket.Bind("tcp://*:1234");
while (true)
{
string q = socket.Recv(Encoding.Unicode);
if (q.CompareTo("ping") == 0) {
socket.Send("pong", Encoding.Unicode);
}
}
}
With this setup, at server side, I can see around 60,000 requests received per second (when client is on the same machine). When on different machines, each new client increases number of requests received at server as expected.
Server Case 2: This is essentially rrbroker from ZMQ guide.
void ReceiveMessages(Context zmqContext, string zmqConnectionString, int numWorkers)
{
List<PollItem> pollItemsList = new List<PollItem>();
routerSocket = zmqContext.Socket(SocketType.ROUTER);
try
{
routerSocket.Bind(zmqConnectionString);
PollItem pollItem = routerSocket.CreatePollItem(IOMultiPlex.POLLIN);
pollItem.PollInHandler += RouterSocket_PollInHandler;
pollItemsList.Add(pollItem);
}
catch (ZMQ.Exception ze)
{
Console.WriteLine("{0}", ze.Message);
return;
}
dealerSocket = zmqContext.Socket(SocketType.DEALER);
try
{
dealerSocket.Bind("inproc://workers");
PollItem pollItem = dealerSocket.CreatePollItem(IOMultiPlex.POLLIN);
pollItem.PollInHandler += DealerSocket_PollInHandler;
pollItemsList.Add(pollItem);
}
catch (ZMQ.Exception ze)
{
Console.WriteLine("{0}", ze.Message);
return;
}
// Start the worker pool; cant connect
// to inproc socket before binding.
workerPool.Start(numWorkers);
while (true)
{
zmqContext.Poll(pollItemsList.ToArray());
}
}
void RouterSocket_PollInHandler(Socket socket, IOMultiPlex revents)
{
RelayMessage(routerSocket, dealerSocket);
}
void DealerSocket_PollInHandler(Socket socket, IOMultiPlex revents)
{
RelayMessage(dealerSocket, routerSocket);
}
void RelayMessage(Socket source, Socket destination)
{
bool hasMore = true;
while (hasMore)
{
byte[] message = source.Recv();
hasMore = source.RcvMore;
destination.Send(message, message.Length, hasMore ? SendRecvOpt.SNDMORE : SendRecvOpt.NONE);
}
}
Where the worker pool's start method is:
public void Start(int numWorkerTasks=8)
{
for (int i = 0; i < numWorkerTasks; i++)
{
QueryWorker worker = new QueryWorker(this.zmqContext);
Task task = Task.Factory.StartNew(() =>
worker.Start(),
TaskCreationOptions.LongRunning);
}
Console.WriteLine("Started {0} with {1} workers.", this.GetType().Name, numWorkerTasks);
}
public class QueryWorker
{
Context zmqContext;
public QueryWorker(Context zmqContext)
{
this.zmqContext = zmqContext;
}
public void Start()
{
Socket socket = this.zmqContext.Socket(SocketType.REP);
try
{
socket.Connect("inproc://workers");
}
catch (ZMQ.Exception ze)
{
Console.WriteLine("Could not create worker, error: {0}", ze.Message);
return;
}
while (true)
{
try
{
string message = socket.Recv(Encoding.Unicode);
if (message.CompareTo("ping") == 0)
{
socket.Send("pong", Encoding.Unicode);
}
}
catch (ZMQ.Exception ze)
{
Console.WriteLine("Could not receive message, error: " + ze.ToString());
}
}
}
}
Could you post some source code or at least a more detailed explanation of your test case? In general the way to build out your design is to make one change at a time, and measure at each change. You can always move stepwise from a known working design to more complex ones.
Most probably the 'ROUTER' is the bottleneck.
Check out these related questions on this:
Client maintenance in ZMQ ROUTER
Load testing ZeroMQ (ZMQ_STREAM) for finding the maximum simultaneous users it can handle
ROUTER (and ZMQ_STREAM, which is just a variant of ROUTER) internally has to maintain the client mapping, hence IMO it can accept limited connections from a particular client. It looks like ROUTER can multiplex multiple clients, only as long as, each client has only one active connection.
I could be wrong here - but I am not seeing much proof to the contrary (simple working code that scales to multi-clients with multi-connections with ROUTER or STREAM).
There certainly is a very severe restriction on concurrent connections with ZeroMQ, though it looks like no one know what is causing it.
I have done done performance testing on calling a native unmanaged DLL function with various methods from C#:
1. C++/CLI wrapper
2. PInvoke
3. ZeroMQ/clrzmq
The last might be interesting for you.
My finding at the end of my performance test was that using the ZMQ binding clrzmq was not useful and produced a factor of 100 performance overhead after I tried to optimize the PInvoke calls within the source code of the binding. Therefore I have used the ZMQ without a binding but with PInvoke calls.these calls must be done with the cdecl convention and with the option "SuppressUnmanagedCodeSecurity" to get most speed.
I had to import just 5 functions which was fairly easy.
At the end the speed was a bit slower than a PInvoke call but with the ZMQ-in my case over "inproc".
This may give you the hint to try it without the binding, if speed is interesting for you.
This is not a direct answer for your question but may help you to increase performance in general.
I am tasked with writing a distributed event managing tool where each client, either a Ruby, C# or Java Client, synchronises all changes with a list of registered clients. I have to use XML-RPC to achieve the goal. My team and I have written up an XML-RPC client and server in each language and will provide the relevant source code below. If you require more code, please let me know.
The problem is that I can get Java and C# to communicate with each other. Ruby can communicate with the others but C# (and maybe Java, haven't tested yet) have problems addressing the Ruby server. I guess the problem is with the Endpoint. First let me give you some code. When reading please be aware that the code is actually written by a team and naming conventions differ a bit:
C# client
Uri _address = new Uri("http://" + _s + ":8000/xmlrpc/EventManagerService");
ChannelFactory<IEventManagerWCF_XML_RPC> _factory = new ChannelFactory<IEventManagerWCF_XML_RPC>(new WebHttpBinding(WebHttpSecurityMode.None), new EndpointAddress(_address));
_factory.Endpoint.Behaviors.Add(new XmlRpcEndpointBehavior());
IEventManagerWCF_XML_RPC _proxy = _factory.CreateChannel();
_proxy will not hold the client for a given URI. Those are stored in a dictionary and used when the need arises to synchronise events. One such synchronisation would happen in the case of a modification;
foreach(IEventManagerWCF_XML_RPC proxy in this.peers.Values)
proxy.modify(_id, _field, _newValue);
Here is an extract from the IEventManagerWCF_XML_RPC interface;
[OperationContract(Action = "EventManagerService.modify")]
bool modify(int id, string field, string newValue);
C# XML RPC service
Uri _baseAddress = new Uri("http://localhost:8000/xmlrpc");
_eventManagerHost = new ServiceHost(typeof(EventManagerService), _baseAddress);
try
{
ServiceEndpoint epXmlRpc = _eventManagerHost.AddServiceEndpoint(typeof(IEventManagerWCF_XML_RPC), new WebHttpBinding(WebHttpSecurityMode.None), "EventManagerService");
epXmlRpc.Behaviors.Add(new XmlRpcEndpointBehavior());
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
_eventManagerHost.Description.Behaviors.Add(smb);
_eventManagerHost.Open();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
_eventManagerHost.Abort();
}
Nothing special here I guess. Lets move on to the Java code!
Java Client
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
try {
config.setServerURL(new URL("http://"+ip+":8000/xmlrpc/EventManagerService"));
}
catch (MalformedURLException e) {
e.printStackTrace();
}
config.setEnabledForExtensions(true);
config.setConnectionTimeout(60 * 1000);
config.setReplyTimeout(60 * 1000);
XmlRpcClient client = new XmlRpcClient();
client.setTransportFactory(new XmlRpcCommonsTransportFactory(client));
client.setConfig(config);
xmlRpcPeers.put(ip, client);
xmlRpcPeers now holds the different clients. They are called as follows;
for(XmlRpcClient peer : this.xmlRpcPeers.values())
{
try {
peer.execute("EventManagerService.modify", params);
} catch (Exception e) {
e.printStackTrace();
}
}
The Java Server has it's own class and is instantiated with a simple new call;
public class Server extends Thread{
/**
* Server port
*/
private static final int port = 8000;
/**
* Starts the XML-RPC server
*/
public void run(){
WebServer webServer = new WebServer(port);
XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer();
PropertyHandlerMapping phm = new PropertyHandlerMapping();
try
{
phm.addHandler("EventManagerService", lu.uni.binfo.ds.EventManager_Java.EventManagerService.class);
}
catch (XmlRpcException e1)
{
e1.printStackTrace();
}
xmlRpcServer.setHandlerMapping(phm);
XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer.getConfig();
serverConfig.setEnabledForExtensions(true);
serverConfig.setContentLengthOptional(false);
try
{
webServer.start();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
Up till now everything seemed to work fine. Adding Ruby to the mix is what gives the most trouble. Here is the relevant code;
Ruby Client
Ruby clients are also stored in a dictionary. It is populated as follows;
#devices_XMLRPC[key] = EventManagerClient_XMLRPC.new(tokens[0]).device
The code for the class is:
class EventManagerClient_XMLRPC
#uri
#device
attr_accessor :uri, :device
def initialize(uri)
#uri = uri
#device = XMLRPC::Client.new2(
uri="http://" << #uri.to_s << ":8000/xmlrpc/EventManagerService", proxy=nil, timeout=30)
end
end
A call to synchronise on modifications looks like this:
#devices_XMLRPC.each_value { |client| client.call("EventManagerService.modify", tokens[0], field, value) }
Ruby Server
server = XMLRPC::Server.new(8000, "127.0.0.1")
server.add_handler("xmlrpc/EventManagerService", EventManagerService.instance)
puts "Server ready!"
server.serve
The EventManagerService class:
class EventManagerService
include Singleton
#manager
def initialize()
#manager = EventManager.instance
end
def modify(id, field, newValue)
puts "modify called"
#manager.modify([id, field, newValue], 1)
end
end
EventManager being the class where all the logic resides.
The error when trying to communicate from C# to Ruby is an EndPointNotFoundException that reads:
There was no endpoint listening at http://ip:8000/xmlrpc/EventManagerService that could accept the message.[...]
I tried fiddling around with the endpoint declaration but cannot seem to get it to work. The Ruby documentation does not help either. I am in need of help!
You could try inspecting the traffic that goes over the line with WireShark. With this tool, you would be able to see the actual HTTP data that is transferred between the different applications. Maybe this provides a hint as to why you have problems communicating between Ruby and C#.