I'm trying to do messaging between programs on separate computers on a local network. The design is basically one "server," multiple "clients," with the server sending messages to the clients and receiving messages from the clients, and the clients receiving messages from the server, and sending/receiving client messages between each other.
I'm trying to use NetMQ for this, but I'm having trouble getting it working on a network. I can run the server and client programs perfectly fine on a single machine, but I'm not sure how to get it to work on a network, and I can't find any NetMQ examples that show this--to be clear, every example I can find uses "localhost" or some other IP address for both the "publisher" and "subscriber" (I'm using a Pub-Sub pattern, but I've seen the same regardless of the pattern used in the example).
Some example code using the client-client framework (this requires an XPub-XSub framework because there are multiple publishers and subscribers):
NetMQPoller poller = new NetMQPoller();
PublisherSocket clientPub = new PublisherSocket("tcp://*:4444");
XPublisherSocket xPub = new XPublisherSocket("tcp://*:5555");
XSubscriberSocket xSub = new XSubscriberSocket("tcp://*:4444");
Proxy proxy = new proxy(xSub, xPub, poller: poller);
proxy.Start();
SubscriberSocket clientSub = new SubscriberSocket("tcp://*:5555");
poller.Add(clientSub);
poller.Add(clientPub);
poller.RunAsync();
If I run this, I get "the application is in break mode" with the following error:
NetMQ.NetMQException HResult=0x80131500 Source=NetMQ StackTrace:
at NetMQ.Core.Transports.Tcp.TcpConnector.OutCompleted(SocketError
socketError, Int32 bytesTransferred) at
NetMQ.Core.IOObject.OutCompleted(SocketError socketError, Int32
bytesTransferred) at NetMQ.Core.Utils.Proactor.Loop() at
System.Threading.ThreadHelper.ThreadStart_Context(Object state) at
System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state, Boolean
preserveSyncCtx) at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state, Boolean
preserveSyncCtx) at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state) at
System.Threading.ThreadHelper.ThreadStart()
If I change the clientSub's address to "tcp://127.0.0.1:5555" it starts fine. However, that won't work because the subscriber needs to receive messages from any machine on the network.
How do I do this with NetMQ? Can anyone provide a working example of this?
By default when you use the constructor of a socket is either bind or connect, depends on the socket type. publisher bind (listen)and subscriber connects. So it does make sense to use * for the subscriber.
You are using tcp transport, so you have to specify an ip address of the remote peer when connecting. If you want to connect to multiple publishers you can either call connect or provide s string with multiple addresses separated by comma.
Bottom line instead of *, localhost or 127.0.0.1 use the ip address of the publisher machine.
You can use the # or > prefixes to override the defaults of the constructor. # to bind and > to connect.
NetMQPoller poller = new NetMQPoller();
PublisherSocket clientPub = new PublisherSocket(">tcp://192.168.0.15:4444");
XPublisherSocket xPub = new XPublisherSocket("#tcp://*:5555");
XSubscriberSocket xSub = new XSubscriberSocket("#tcp://*:4444");
Proxy proxy = new proxy(xSub, xPub, poller: poller);
proxy.Start();
SubscriberSocket clientSub = new SubscriberSocket(">tcp://192.168.0.15:5555");
poller.Add(clientSub);
poller.Add(clientPub);
poller.RunAsync();
192.168.0.15 is my ip address. You need to change it to your ip.
I have a simple example. My "server" code runs on a Amazon EC2 instance. It listens on port 11000 and responds to a single message.
using System;
using NetMQ;
using NetMQ.Sockets;
class Program
{
private static void Main()
{
using (var server = new ResponseSocket("#tcp://*:11000"))
{
// the server receives, then sends
Console.WriteLine("From Client: {0}", server.ReceiveFrameString());
server.SendFrame("Hi Back");
Console.ReadKey();
}
}
}
My Client code runs on my PC:
using System;
using NetMQ;
using NetMQ.Sockets;
class Program
{
private static void Main()
{
using (var client = new RequestSocket(">tcp://3.13.186.250:11000"))
{
client.SendFrame("Hello");
Console.WriteLine("From Server: {0}", client.ReceiveFrameString());
Console.ReadKey();
}
}
}
Related
my requirement is to update the down server host with other available server name in a table in db. This record will be used a windows service which installed on all the servers on the network to run a scheduled task. need help to get server host name which is down on a network i tried using ping, tcpclient and WMI and everytime when a server is down on my network getting below error:
Message: GetDataBaseHandleWithAccess - INNER
Exception:System.ComponentModel.Win32Exception (0x80004005): The RPC
server is unavailable
Message: GetDataBaseHandleWithAccessServer Health
CheckupSystem.InvalidOperationException: Cannot open Service Control
Manager on computer 'VW144444'. This operation might require other
privileges. ---> System.ComponentModel.Win32Exception: The RPC server
is unavailable --- End of inner exception stack trace ---
below are link which i referred and tried
detect-if-machine-is-online-or-offline-using-wmi-and-c-sharp
csharp-check-if-machine-is-online-or-offline: using Ping Service
how-to-check-a-server-is-alive : using TcpClient
I think you can use the exception itself to handle the logic.If the exception contains RPC server unavailable, you can consider it as a server not up. A better approach would be use the ping but ping can be disabled (it uses ICMP echo and ICMP protocol can be disabled by your network administrator) .So if it is internal domain servers,chances are that it will be enabled. So you can use combination of all three techniques Ping, TcpClient and WMI.
My first preference is using Ping
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
namespace PingTest
{
public class PingExample
{
// args[0] can be an IPaddress or host name.
public static void Main (string[] args)
{
Ping pingSender = new Ping ();
PingOptions options = new PingOptions ();
// Use the default Ttl value which is 128,
// but change the fragmentation behavior.
options.DontFragment = true;
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes (data);
int timeout = 120;
PingReply reply = pingSender.Send (args[0], timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
}
}
}
}
And if you are checking some particular service is up or not e.g web service ,then use TcpClient to connect to the port this service is listening same SO question you linked How to check a server is alive?
I have a WCF Service and WCF Client working on Duplex Channel using netTCPBinding.
I store connected users in a dictionary ( Dictionary<int userID,CallbackInstance instance> )
When user Disconnect regularly, call Disconnect from service and I remove the user from my connected user list. It works fine.
but when client pc disconnects unregularly, client could not call Disconnect method, so client still in connected user list, that's the problem. Because when my WCF server check server for online users for a callback, server try to call client's callback method, but client is not available, and my WCF Server App crash.
Is it possible to check client status before calling callback instance?
Make sure all the properties timeouts are set to automatically remove inactive clients then catch the timeout exception in a try catch block and remove it from your dictionary.
I fix this with:
1.Method to ping from client to server to keep the conection active for each 30 seconds.
2.On server binding, ReceiveTimeout with 1 minute.
3.Foreach callback created a IcommunicationObject, using the Closed event to remove the inactive client.
//Adding a client callback
OperationContext context = OperationContext.Current;
ICallback callback = context.GetCallbackChannel();
ICommunicationObject obj = (ICommunicationObject)callback;
obj.Closed += new EventHandler(obj_Closed);
//Event for inactive clients
void obj_Closed(object sender, EventArgs e)
{
if (_callbacks.ContainsValue(((ITecnobelRemoteServiceCallback)sender)))
{
var item = _callbacks.First(kvp => kvp.Value == ((ITecnobelRemoteServiceCallback)sender));
_callbacks.Remove(item.Key);
treeViewClients.Nodes.RemoveByKey(item.Key.Id);
treeViewClients.Refresh();
_registeredUsers--;
listBoxStatus.Items.Add(String.Format("Usuário {0} estava inativo e foi removido", item.Key.Id));
}
}
I'm following this tutorial, and I keep getting an exception calling QueueClient.Send().
First off, here's my connection string setting in the App.Config (with {computername} replaced by the actual machine name):
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://{computername}/ServiceBusDefaultNamespace;StsEndpoint=https://{computername}:9355/ServiceBusDefaultNamespace;RuntimePort=9354;ManagementPort=9355" />
Here's the code I'm running:
NamespaceManager namespaceManager = NamespaceManager.Create();
TokenProvider nameSpaceManagerTokenProvider = TokenProvider.CreateWindowsTokenProvider(
new List<Uri>() { namespaceManager.Address }, new NetworkCredential(user, password));
TokenProvider messagingToken = TokenProvider.CreateWindowsTokenProvider(
new List<Uri>() { namespaceManager.Address }, new NetworkCredential(user, password));
namespaceManager.Settings.TokenProvider = nameSpaceManagerTokenProvider;
MessagingFactorySettings messageFactorySettings = new MessagingFactorySettings {TokenProvider = messagingToken};
MessagingFactory messagingFactory = MessagingFactory.Create(namespaceManager.Address, messageFactorySettings);
if (namespaceManager.QueueExists(QueueName))
{
namespaceManager.DeleteQueue(QueueName);
}
QueueDescription qd = new QueueDescription(QueueName);
namespaceManager.CreateQueue(qd);
QueueClient myQueueClient = messagingFactory.CreateQueueClient(QueueName);
BrokeredMessage sendMessage = new BrokeredMessage("Hello, World!");
myQueueClient.Send(sendMessage); // <---- This is where I'm getting the exception
The queue is deleted/created without a problem. Calling the .Send() method gives me the following error:
Microsoft.ServiceBus.Messaging.MessagingCommunicationException
"The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:59.9579976'."
The inner exception is simply "An existing connection was forcibly closed by the remote host"
Here's the stack trace:
at Microsoft.ServiceBus.Messaging.Sbmp.SbmpMessageSender.EndSendCommand(IAsyncResult result)
at Microsoft.ServiceBus.Messaging.Sbmp.SbmpMessageSender.OnEndSend(IAsyncResult result)
at Microsoft.ServiceBus.Messaging.Sbmp.SbmpMessageSender.OnSend(TrackingContext trackingContext, IEnumerable`1 messages, TimeSpan timeout)
at Microsoft.ServiceBus.Messaging.MessageSender.Send(TrackingContext trackingContext, IEnumerable`1 messages, TimeSpan timeout)
at Microsoft.ServiceBus.Messaging.MessageSender.Send(BrokeredMessage message)
at Microsoft.ServiceBus.Messaging.QueueClient.Send(BrokeredMessage message)
at SBDemo.Program.Main(String[] args) in c:\Users\hartez\Documents\bitbucket\SBDemo\SBDemo\Program.cs:line 51
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
I'm currently running both the client code and the Service Bus on a Windows 7 64-bit dev box. I originally ran Service Bus on a 2012 Server machine and had the same problem.
The problem occurs with both the WindowsTokenProvider and the OauthTokenProvider. The user account is an administrator (in hopes that this was just a permissions issue); that doesn't seem to help. I've also tried this with the Windows Firewall deactivated, but that didn't help, either.
I enabled the Analytic and Debug logs in the Event Viewer, but I'm not seeing an anything in those logs to suggest what the problem might be.
If anyone has any suggestions on what might be wrong, or on other ways to debug this, I'd very much appreciate it.
Glad you have figured this out.
MessagingFactory messagingFactory = MessagingFactory.Create(namespaceManager.Address, messageFactorySettings);
The issue you had in your original code is that it is using the NamespaceManager address for the MessagingFactory. The MessagingFactory uses a different port than the NamespaceManager.
NamespaceManager is used for management (CRUD) operations and SB has a management endpoint for it.
MessagingFactory is used for runtime operations (Send/Receive/..) and SB has a runtime endoiunt for it.
QueueClient.Create(QueueName) internally creates a messaging factory and uses the default address and port for runtime operations.
Finally figured this out, putting this here so anyone else in the same boat can avoid wrestling with this problem:
Apparently, the problem is somewhere in the use of MessagingFactory in the examples. I'm not really sure what it's even for (maybe handling things like AMQP, which isn't really supported by Service Bus yet?), but you don't need it. You can create the client using QueueClient.Create(). I revised the code to look like this, and now it works just fine:
NamespaceManager namespaceManager = NamespaceManager.Create();
TokenProvider nameSpaceManagerTokenProvider = TokenProvider.CreateWindowsTokenProvider(
new List<Uri>() { namespaceManager.Address }, new NetworkCredential(user, password));
namespaceManager.Settings.TokenProvider = nameSpaceManagerTokenProvider;
if (namespaceManager.QueueExists(QueueName))
{
namespaceManager.DeleteQueue(QueueName);
}
QueueDescription qd = new QueueDescription(QueueName);
namespaceManager.CreateQueue(qd);
QueueClient myQueueClient = QueueClient.Create(QueueName);
BrokeredMessage sendMessage = new BrokeredMessage("Hello, World!");
myQueueClient.Send(sendMessage);
I am just learning WCF, trying out different things.
I have set up the following service:
[ServiceBehavior(IncludeExceptionDetailInFaults = true ,
InstanceContextMode = InstanceContextMode.Single)]
public class TestService : ITestService
{
// This operation is defined as OneWay.
public void Throws()
{
throw new ArgumentException();
}
}
I use it from my client like so:
var baseAddress = new Uri("net.pipe://localhost/hello");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(TestService), baseAddress))
{
var netBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
host.AddServiceEndpoint(typeof(ITestService), netBinding, baseAddress);
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
// Create client channel.
var channel = ChannelFactory<ITestService>.CreateChannel(netBinding, new EndpointAddress(baseAddress));
((ICommunicationObject)channel).Open();
try
{
foreach (var i in Enumerable.Range(0, 5000))
{
// channel dies after a few attempts.
channel.Throws();
}
}
The method Throws is defined as IsOneWay = true, meaning it does not propogate any message back to the client (including errors).
When running in a loop, the communication object faults after some runs.
I cannot figure out the cause of this.
The exception details:
System.ServiceModel.CommunicationException: There was an error writing
to the pipe: The pipe is being closed. (232, 0xe8). --->
System.IO.PipeException: There was an error writing to the pipe: The
pipe is being closed. (232, 0xe8). at
System.ServiceModel.Channels.PipeConnection.StartSyncWrite(Byte[]
buffer, Int32 offset, Int32 size, Object& holder) at
System.ServiceModel.Channels.PipeConnection.Write(Byte[] buffer, Int32
offset, Int32 size, Boolean immediate, TimeSpan timeout, BufferManager
bufferManage r) --- End of inner exception stack trace ---
Note that if i change the body of Throws method to something else, like Console.WriteLine, everything is running fine.
EDIT: I have uploaded the sample project to my SkyDrive: http://sdrv.ms/NumUbR
In case someone wants to compile it locally and see if it behaves the same.
You are simply exceeding the available bandwidth at some point. It’s likely the pipe, but it might also be in the WCF stack… processing exceptions is expensive and you are doing 5000 of them in as tight a loop as possible. Changing from an exception to a WriteLine() that returns nothing fixes the problem because it dramatically reduces the required bandwidth/processing. (I do see you mentioned OneWay, but I don't think it changes much. Even if the exception isn't returned, it still has to be processed).
Try changing InstanceContextMode to PerCall. That's the standard setting for “high volume” services. It will relieve some of the congestion.
Also to address the comments, hosting the service like this is fine. The ServiceHost will manage its own threads.
I have a IIS hosted WCF service that is configured with a WebHttpBinding. It is the same default WCF service VS2008 creates when you create a new one. The only changes I have made is to allow it to be called from javascript.
In the test method GetData(int value), i return a string that displays the remote clients IP address, port, and useragent. Everything works fine, except I am not expecting the port number it is returning. I am expecting port 80, but instead get something like 49353. Below is the method I am calling.
public string GetData(int value)
{
OperationContext context = OperationContext.Current;
MessageProperties messageProperties = context.IncomingMessageProperties;
RemoteEndpointMessageProperty endpointProperty = messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return string.Format("Hello {0}! \r\naddress: {1}\r\nport: {2}\r\nuseragent: {3}",
value, endpointProperty.Address, endpointProperty.Port, WebOperationContext.Current.IncomingRequest.UserAgent);
}
Like I said, i am invoking this method from javascript. IIS is configured for port 80, so I am not sure why the port is being reported wrong.
I think you might have this a bit backwards. Port 80 is where you are listening, but once that happens, it passes that off to a new ephemeral port to handle that connection.
Also, the client is also probably using an ephemeral port to open the connection to port 80. So the client's port is likely some "random" port number outside the typical "fixed" port range (0-1024).