.Net Remoting: Invoke Method on client from Server - c#

i'm trying to write a small chat server using .Net Remoting in C#.
The connection to the Server from the client works nicely, but as soon as i try to "broadcast" a sent message to all other clients (or even the same client), the server throws an exception.
"Der Remoteproxy hat keine Channelsenke, d. h., der Server besitzt keine registrierten Serverchannel oder die Anwendung hat keinen passenden Clientchannel, um mit dem Server zu kommunizieren."
what translates to
"This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server."
How would i change this?
Here ( https://www.dropbox.com/s/y40cdv3lopsf6d7/Chatprojekt.zip ) you can find a copy of the full project, but to help others who have the same problem, i'll explain what i did.
On the server i opened a TcpChannel on a specific port, made an interface for both,the server and the client. I connect to the server and with a method,i pass the client instance to the server.
Both implementations of the interfaces implement MarshalByRefObj to use Proxies for method invokation.
Thanks already a lot for helping me

I gave up on doing real .NET remoting in favor of WCF a long time ago. Recently I've been using RemotingLite and my own variant of it which also supports named pipes called DuoVia.Net. You simply write your interface contract and DTO's, share those in an assembly on client and server and write your implementation on the server side.
No client to produce. These libraries produce their own through reflection and dynamic assembly generation by emitting IL. Cool stuff. Have a look at how easy the client side is:
class Program
{
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
var ipEndpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8098);
var from = 0;
var to = 500;
Parallel.For(from, to, index =>
{
using (var client = new NetTcpTesterProxy(ipEndpoint))
{
var id = client.GetId("test1", 3.314, 42);
var response = client.Get(id, "mirror", 4.123, 42);
var list = client.GetItems(id);
}
});
sw.Stop();
var msperop = sw.ElapsedMilliseconds / 1500.0;
Console.WriteLine("tcp: {0}, {1}", sw.ElapsedMilliseconds, msperop);
Thread.Sleep(5000);
var pipeName = "DuoViaTestHost";
sw = Stopwatch.StartNew();
Parallel.For(from, to, index =>
{
using (var client = new NetNpTesterProxy(new NpEndPoint(pipeName)))
{
var id = client.GetId("test1", 3.314, 42);
var response = client.Get(id, "mirror", 4.123, 42);
var list = client.GetItems(id);
}
});
sw.Stop();
msperop = sw.ElapsedMilliseconds / 1500.0;
Console.WriteLine("pip: {0}, {1}", sw.ElapsedMilliseconds, msperop);
Console.ReadLine();
}
}
Have fun!

Related

Avoiding ElasticSearch error 503 Server Unavailable: Use of WaitForStatus

When I start my program, I run the ElasticSearch Service and check if an Index exists and if there is any documents, let's say I just run the ES service and I have these two functions:
public ElasticClient getElasticSearchClient()
{
ConnectionSettings connectionSettings = new Nest.ConnectionSettings(new Uri("http://localhost:9200"))
.DefaultIndex("myindex")
.DisableDirectStreaming();
ElasticClient client = new ElasticClient(connectionSettings);
//var health = client.Cluster.Health("myindex", a => (a.WaitForStatus(WaitForStatus.Yellow)).Timeout(50));
return client;
}
public void checkElasticsearchIndex()
{
var client = getElasticSearchClient();
var health = this.client.Cluster.Health("myindex", a => (a.WaitForStatus(WaitForStatus.Yellow)));
CountResponse count = client.Count<myobject>();
if (!client.Indices.Exists("myindex").IsValid || count.Count == 0)
{
BulkWriteAllToIndexES(client);
}
}
Inside the checkElasticsearchIndex function,
The count operation fails with the following error message:
OriginalException: Elasticsearch.Net.ElasticsearchClientException: The remote server returned an error: (503) Server Unavailable.. Call: Status code 503 from: GET /myindex/_count. ServerError: Type: search_phase_execution_exception Reason: "all shards failed" ---> System.Net.WebException: The remote server returned an error: (503) Server Unavailable.
The Health fails as well:
OriginalException: Elasticsearch.Net.ElasticsearchClientException: Unable to connect to the remote server. Call: Status code unknown from: GET /_cluster/health/myindex?wait_for_status=yellow ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:9200
As you can see, I have tried the Cluster WaitForStatus, but it didn't work.
My question: is there any way to wait until client/cluster/nodes are ready and not get any exception?
It sounds like you're starting the Elasticsearch process at the same time as starting your program, but Elasticsearch takes longer than your program to be ready.
If that's the case, you may be interested in using the same abstractions that the .NET client uses for integration tests against Elasticsearch. The abstractions read output from the Elasticsearch process to know when it is ready, and block until this happens. They're available on an AppVeyor CI package feed (with plans to release them to Nuget in the future).
There are some examples of how to spin up a cluster with the abstractions. For single node, it would be something like
using System;
using Elastic.Managed.Configuration;
using Elastic.Managed.ConsoleWriters;
using Elastic.Managed.FileSystem;
namespace Elastic.Managed.Example
{
class Program
{
static void Main(string[] args)
{
var version = "7.5.1";
var esHome = Environment.ExpandEnvironmentVariables($#"%LOCALAPPDATA%\ElasticManaged\{version}\elasticsearch-{version}");
using (var node = new ElasticsearchNode(version, esHome))
{
node.SubscribeLines(new LineHighlightWriter());
if (!node.WaitForStarted(TimeSpan.FromMinutes(2))) throw new Exception();
// do your work here
}
}
}
}
This assumes that Elasticsearch 7.5.1 zip has been downloaded already, and exists at %LOCALAPPDATA%\ElasticManaged\7.5.1\elasticsearch-7.5.1. There are more complex examples of how to integrate this into tests with xUnit.
You can use the EphemeralCluster components to download, configure and run Elasticsearch
var plugins = new ElasticsearchPlugins(ElasticsearchPlugin.RepositoryAzure, ElasticsearchPlugin.IngestAttachment);
var config = new EphemeralClusterConfiguration("7.5.1", ClusterFeatures.XPack, plugins, numberOfNodes: 1);
using (var cluster = new EphemeralCluster(config))
{
cluster.Start();
var nodes = cluster.NodesUris();
var connectionPool = new StaticConnectionPool(nodes);
var settings = new ConnectionSettings(connectionPool).EnableDebugMode();
var client = new ElasticClient(settings);
Console.Write(client.CatPlugins().DebugInformation);
}

Multiple Conversations For Direct Line Client

I'm trying to use the Microsoft.Bot.Connector.DirectLine .NET client to connect to my Direct Line Channel. My client application will have many conversations open at once (like 1000+).
What I'm trying to do is efficiently create a single Direct Line client object which can receive messages for all my conversations and NOT have a single client per conversation.
This below code is from:
https://learn.microsoft.com/en-us/azure/bot-service/bot-service-channel-directline-extension-net-client?view=azure-bot-service-4.0
The problem is that to create a new conversation I need to create a new client which I think would eventually exhaust use up a lot of sockets. Does anyone know if I can create a single connection and then listen for multiple conversations?
Thanks
static async Task Main(string[] args)
{
Console.WriteLine("What is your name:");
var UserName = Console.ReadLine();
var tokenClient = new DirectLineClient(
new Uri(endpoint),
new DirectLineClientCredentials(secret));
var conversation = await tokenClient.Tokens.GenerateTokenForNewConversationAsync();
var client = new DirectLineClient(
new Uri(endpoint),
new DirectLineClientCredentials(conversation.Token));
await client.StreamingConversations.ConnectAsync(
conversation.ConversationId,
ReceiveActivities);
var startConversation = await client.StreamingConversations.StartConversationAsync();
var from = new ChannelAccount() { Id = startConversation.ConversationId, Name = UserName };
var message = Console.ReadLine();
while (message != "end")
{
try
{
var response = await client.StreamingConversations.PostActivityAsync(
startConversation.ConversationId,
new Activity()
{
Type = "message",
Text = message,
From = from,
ChannelData = new Common.ChannelData() { FromNumber = "+17081234567"}
});
}
catch (OperationException ex)
{
Console.WriteLine(
$"OperationException when calling PostActivityAsync: ({ex.StatusCode})");
}
message = Console.ReadLine();
}
Console.ReadLine();
}
public static void ReceiveActivities(ActivitySet activitySet)
{
if (activitySet != null)
{
foreach (var a in activitySet.Activities)
{
if (a.Type == ActivityTypes.Message && a.From.Id == "MyBotName")
{
Console.WriteLine($"<Bot>: {a.Text}");
}
}
}
}
I think using the Direct Line streaming extensions would be problematic for your purposes. I'm guessing your custom SMS channel would itself be an app service. Since an app service can (and probably should, in your case) be scaled so that multiple instances are running simultaneously, suppose two SMS messages from the same conversation go to two instances of your channel. In addition to having each instance of your channel using many web sockets to talk to many bots, multiple instances of your channel may use duplicated web sockets to talk to the same bot. There's also the problem of each bot itself needing to support streaming extensions.
Rather than using using Direct Line streaming extensions, you might consider using traditional Direct Line. This would involve receiving activities from the bots by polling a Direct Line endpoint.
Since Direct Line is a channel itself that you'd be using on top of your own channel, you might also consider cutting out Direct Line altogether. That way you wouldn't have two channels between the user and the bot. You could send HTTP requests to each bot's endpoint directly, and the activities the bots would receive would contain the service URL for your channel, allowing your channel to receive messages from the bots.

StreamInsight: Using a local Observer for a RemoteObservable

I've been playing with StreamInsight v2.3 and the newer Rx capabilities it provides. I'm investigating the use of SI for an Event Sourcing implementation. I've tweaked some of the MSDN sample code to get the following:
code for the server process:
using (var server = Server.Create("Default"))
{
var host = new ServiceHost(server.CreateManagementService());
host.AddServiceEndpoint(typeof(IManagementService), new WSHttpBinding(SecurityMode.Message), "http://localhost/SIDemo");
host.Open();
var myApp = server.CreateApplication("SIDemoApp");
var mySource = myApp.DefineObservable(() => Observable.Interval(TimeSpan.FromSeconds(1))).ToPointStreamable(x => PointEvent.CreateInsert(DateTimeOffset.Now, x), AdvanceTimeSettings.StrictlyIncreasingStartTime);
mySource.Deploy("demoSource");
Console.WriteLine("Hit enter to stop.");
Console.ReadLine();
host.Close();
}
code for the client process:
using (var server = Server.Connect(new System.ServiceModel.EndpointAddress(#"http://localhost/SIDemo")))
{
var myApp = server.Applications["SIDemoApp"];
var mySource = myApp.GetObservable<long>("demoSource");
using (var mySink = mySource.Subscribe(x => Console.WriteLine("Output - {0}", x)))
{
Console.WriteLine("Hit enter to stop.");
Console.ReadLine();
}
}
Trying to run this produces the following error:
Reading from a remote
'System.Reactive.Linq.IQbservable`1[System.Int64]' is not supported.
Use the 'Microsoft.ComplexEventProcessing.Linq.RemoteProvider.Bind'
method to read from the source using a remote observer.
The sample code I started with defines an observer and sink and binds it in the StreamInsight server. I'm trying to keep the observer in the client process. Is there a way to set up an observer in the client app for a remote StreamInsight source? Does this have to be done through something like a WCF endpoint in the server that is observed by the client?
Actualy error is directing to the solution. You need 'bind' to the source.
Please check the snippet below:
//Get SOURCE from server
var serverSource = myApp.GetStreamable<long>("demoSource");
//SINK for 'demoSource'
var sinkToBind = myApp.DefineObserver<long>( ()=> Observer.Create<long>( value => Console.WriteLine( "From client : " + value)));
//BINDING
var processForSink = serverSource.Bind(sinkToBind).Run("processForSink");
Also note that, sink will run on server, not like I guessed at first that it will run on client. If you look to console apps for both server and client, console output is writing to server app.
If even there is a way to run sink on client, I don't know and I like to know that too.

Passing a stream to a method via .NET remoting

Let me briefly describe the situation.
First, I have a WCF RestFul webservice with this method :
[WebInvoke(UriTemplate = "/Dynamic/{*sParameter}", Method = "POST", ResponseFormat = WebMessageFormat.Json)]
public string ExecuteWebAPIRequest(string sParameter, Stream streamPost)
{
...
var oClientFormatSinkProvider = new BinaryClientFormatterSinkProvider();
IDictionary aoProps = new Hashtable();
aoProps["port"] = 4626;
aoProps["timeout"] = "-1";
aoProps["name"] = "clientChan";
TcpClientChannel channel = new TcpClientChannel(aoProps, oClientFormatSinkProvider);
ChannelServices.RegisterChannel(channel, false);
//-- Call Bridge
string result = GetBridgeObject().ExecuteWebAPIRequest(sIpAddress, streamPost, sParameter);
//-- Return result
return result ?? "";
}
here is the content of the GetBridgeObject() method (that would be the remoting client):
private IBridge GetBridgeObject()
{
return (IBridge)Activator.GetObject(typeof(IBridge), "tcp://localhost:4626/RemoteBridge");
}
Next, there is the bridge process containing this method (that would be the remoting server):
public void StartService()
{
//-- Initialize .NET remoting
var oServerFormatSinkProvider = new BinaryServerFormatterSinkProvider();
oServerFormatSinkProvider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary aoProps = new Hashtable();
aoProps["port"] = 4626;
aoProps["timeout"] = "-1";
aoProps["name"] = "serverChan";
TcpServerChannel channel = new TcpServerChannel(aoProps, oServerFormatSinkProvider);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof (Bridge), "RemoteBridge", WellKnownObjectMode.Singleton);
}
And finaly, in the remote bridge object, this method :
public string WUPP_ExecuteWebAPIRequestI(string sPPInstanceName, Stream oInputStream, string sParameter)
{
...
int read = oInputStream.Read(buffer, 0, buffer.Length); //That's where the problem is
...
}
As stated in the code snippet, the problem occurs when I try to read the stream, i get this error :
Exception: System.Runtime.Remoting.RemotingException: This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server.
at System.Runtime.Remoting.Proxies.RemotingProxy.InternalInvoke(IMethodCallMessage reqMcmMsg, Boolean useDispatchMessage, Int32 callType)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(IMessage reqMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.IO.Stream.Read(Byte[] buffer, Int32 offset, Int32 count)
I know for sure I can pass streams via .NET remoting because in the bridge, there are other methods that return streams and that work well.
I guess the problem is somewhere in the remoting server or client when I register the channels but after two days of research and tests, i still haven't found an answer.
Thanks in advance for your help.
Since I had a similar problem and needed a solution as well, I finally found a solution: the client channel needs to be registered differently to make it work.
// Creating a custom formatter for a TcpChannel sink chain.
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
provider.TypeFilterLevel = TypeFilterLevel.Full;
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("typeFilterLevel", "Full");
dict.Add("port", "0");
ChannelServices.RegisterChannel(new TcpChannel(dict, clientProvider, provider), false);
The interessting things here are
the channel is bidirectional
port 0 tells remoting framework to decide which port it should use.
for up- and download of streams the typeFilterLevel Full needs to be
set.
Would be interested to know why my answer got a downvote as the solution works in production code.

UDP in C# works on Windows but not Linux

I'm using mono to build a C# program that needs to send and receive using UDP. Currently my implementation works as expected on Windows but I have issues getting communication to work with my Ubuntu or Fedora systems.
Windows can broadcast and receive it's own datagrams.
Ubuntu can broadcast and receive it's own datagrams. It's broadcasts are received by Windows but it doesn't see datagrams broadcast by Windows.
Fedora can broadcast but does not receive datagrams from anywhere (not even itself). It's broadcasts are received by Windows.
When datagrams fail to reach either of the linux machines, the 'receive' function is never fired.
This is what I have so far:
int _port = 4568;
var server = new UdpClient(_port);
var send_UDP = new UdpClient();
The receive method uses the asynchronous calls of the UDPClient;
private static void receive()
{
server.BeginReceive(new AsyncCallback(receive), null);
}
private static void receive(IAsyncResult o)
{
try
{
// I'm told that port = 0 should receive from any port.
var sender = new IPEndPoint(IPAddress.Any, 0);
var data = server.EndReceive(o, ref sender);
receive();
var str = new string(Encoding.ASCII.GetChars(data));
postmessage(sender.Address.ToString() + ":" + sender.Port.ToString() + " > " + str);
}
catch {}
}
And the send method;
public static void send(string message)
{
var target = new IPEndPoint(IPAddress.Parse("255.255.255.255"), _port);
byte[] data = Encoding.ASCII.GetBytes(message);
send_UDP.Send(data, data.Length, target);
}
After some testing with Fedora, it seems to be an issue with the use of 255.255.255.255 to broadcast. Is there some other way to do this?
I already specified this in a comment but placing this as an answer since you may have overlooked it and no answers seem to be forthcoming.
Instead of using 255.255.255.255 for broadcast use your local IP subnet's broadcasting address (for instance 192.168.0.255 on a 192.168.0.1/24 subnet). The 255.255.255.255address will not be forwarded by a router (this is relevant if there are multiple subnets at your clients' sites) whereas a directed broadcast can be forwarded (if so configured). It used to be the case that routers would forward directed broadcasts per default but this was changed in RFC2644 so don't bet the farm on it ;).
Here's an example of calculating the directed IPV4 broadcast address per adapter:
public static void DisplayDirectedBroadcastAddresses()
{
foreach (var iface in NetworkInterface.GetAllNetworkInterfaces()
.Where(c => c.NetworkInterfaceType != NetworkInterfaceType.Loopback))
{
Console.WriteLine(iface.Description);
foreach (var ucastInfo in iface.GetIPProperties().UnicastAddresses
.Where(c => !c.Address.IsIPv6LinkLocal))
{
Console.WriteLine("\tIP : {0}", ucastInfo.Address);
Console.WriteLine("\tSubnet : {0}", ucastInfo.IPv4Mask);
byte[] ipAdressBytes = ucastInfo.Address.GetAddressBytes();
byte[] subnetMaskBytes = ucastInfo.IPv4Mask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length) continue;
var broadcast = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcast.Length; i++)
{
broadcast[i] = (byte)(ipAdressBytes[i] | ~(subnetMaskBytes[i]));
}
Console.WriteLine("\tBroadcast: {0}", new IPAddress(broadcast).ToString());
}
}
}

Categories