I want to build an actor system that has a common server at a static IP address and port. There will be many clients that know the server's address. The server doesn't know the IP addresses of the clients.
Configuration of the server:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "46.38.232.161"
port = 2552
}
}
}
Configuration of the client:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
port = 2553
}
}
}
A client may come from the entire internet. Now I want to send messages from actors on the client to actors on the server. How does the server know, where to send back his messages? When I send ActorPaths to the server, so he would know the corresponding client's address, these don't contain the client's IP address.
akka.remote.netty.hostname property may be set inside your application.conf, to a reachable IP address or resolvable name, in a case you want to communicate across the network. Actually your "client" nodes will also be a servers, when using Akka.
In a case if address is unknown at app start, consider this code fragment from Akka documentation:
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
val customConf = ConfigFactory.parseString("""
akka.actor.deployment {
/my-service {
router = round-robin
nr-of-instances = 3
}
}
""")
// ConfigFactory.load sandwiches customConfig between default reference
// config and default overrides, and then resolves it.
val system = ActorSystem("MySystem", ConfigFactory.load(customConf))
Actor has a method called sender that can be called in the actor's receive method to get a reference to the actor (ActorRef) that sent the current message. The ActorRef has an ActorPath which has a RootActorPath which has an Address which contains the host and port of the ActorSystem where the actor lives.
Related
I have UDP server to listen messages, and I need to bind to device IP address. Other devices send messages on the UDP server. I use next:
InetAddress address = InetAddress.getLocalHost(); // gives 127.0.1.1 (1)
address = InetAddress.getLoopbackAddress(); // 127.0.0.1 (2)
address = InetAddress.getByName("123.45.67.89"); // the same (3)
address = InetAddress.getByName("localhost"); // (4)
I run my code on different environments and see next:
On my local machine with win10 .getByName("localhost") works and .getLocalHost() not worked. Also other devices (emulators) send messages on "localhost".
On other remote PC with Win7 and some IP I'm using (1) and it works. Physical devices send messages on server IP and it process them. Also, I'm using bridge to allow devices communicate with each other, i.e. devices placed in different networks (I don't understand this configuration, it is not my).
On this remote PC but in Linux I can set address only manually, i.e. variant (3). But I need specify it automatically.
I can't get the correct server address by any method. How I can get device address? Maybe there are some another methods or API?
UPD: I'm using netty udp server with standard configuration:
#Override
public void run() {
final NioEventLoopGroup group = new NioEventLoopGroup();
try {
log.info("Started listening logs ...");
final Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChannelInitializer<NioDatagramChannel>() {
#Override
public void initChannel(final NioDatagramChannel nioDatagramChannel) {
ChannelPipeline channelPipeline = nioDatagramChannel.pipeline();
channelPipeline.addLast(encryptedPacketHandlerChain);
}
});
// Bind and start to accept incoming connections.
InetAddress address = InetAddress.getLocalHost();
System.out.println("InetAddress..getLocalHost() == " + address.getHostAddress());
address = InetAddress.getLoopbackAddress();
System.out.println("InetAddress.getLoopbackAddress == " + address.getHostAddress());
address = InetAddress.getByName(ip);
System.out.println("InetAddress.getByName " + ip + " == " + address.getHostAddress());
bootstrap.bind(address, LOG_PORT).sync().channel().closeFuture().await();
} catch (Exception e) {......
A host may have several network interfaces connected to different networks, and the bind address tells the system which interface you want to listen on. This is typically configured by the user of your application (system administrator) because the networks have different purposes (for example, data plane vs control plan: one network used by system and network admins to control the machine, another network used for production traffic)
If you don't know which interface you should listen on, you can listen on all local interfaces. You do that by binding to the special 0.0.0.0 or :: IP address.
One way you can create the 0.0.0.0 address by first creating a SocketAddress with the InetSocketAddress(int port) constructor, and then retrieving the address from it:
InetAddress anyAddress = new InetSocketAddress(LOG_PORT).getAddress();
Another way is creating the the address directly:
InetAddress anyAddress = InetAddress.getByAddress(new byte[4]);
I need to create a java application that functions similar to WireShark in that it is able to listen to UDP traffic. But I need to know more about the datagram than just the data, I need to know the sender's IP and mac address. It there a way to accomplish this in Java?
Here is a screen shot of what I am talking about from Wireshark
WireShark Image
Notice the Ethernet II stack has the mac and the Internet Protocoal Version 4 has Src Ip.
The use case of this is that there are multiple devices on the network emitting data (as in this msg is STS:ANT:OK:8). But I need to know what the mac and IP is of this sender so I can categorize the msgs by sender and mac. (technically I can have duplicate IPs on the network.) So both are needed. This also allows me to show error cases where this is occurring.
IP protocol is underlying tool for UDP/TCP or other layers. You may need to capture the packets and listen to specific port (filter IP messages for UDP port)
Otherwise you can use external server commands to get it done more efficiently.
Here is an example (from How could I sniff network traffic in Java?):
public static void main(String[] args) throws Exception {
File f = new File("sample.pcap");
EthernetDecoder eth = new EthernetDecoder();
IpDecoder ip = new IpDecoder();
TcpDecoder tcp = new TcpDecoder(new TcpPortProtocolMapper());
UdpDecoder udp = new UdpDecoder(new UdpPortProtocolMapper());
eth.register(EthernetType.IPV4, ip);
ip.register(InternetProtocol.TCP, tcp);
ip.register(InternetProtocol.UDP, udp);
PcapInputStream is = new PcapFileInputStream(f);
while (true) {
// getPacket() will throws EOFException and you should call is.close()
PcapPacket packet = is.getPacket();
eth.decode(packet);
}
}
I'm begininng to use Mqtt and I have a hard time with handling an unreliable network.
I'm using a Paho Java Client (in groovy) to publish messages to a distant Mosquitto Broker.
Is there a way, when the broker is unreachable, to have the Paho client persist the message and automatically re-connect to the broker and publish the locally stored messages ? Do I have to handle everything myself, using for example a local broker ?
Here is my client building code
String persistenceDir = config['persistence-dir'] ?: System.getProperty('java.io.tmpdir')
def persistence = new MqttDefaultFilePersistence(persistenceDir)
client = new MqttAsyncClient(uri, clientId, persistence)
client.setCallback(this)
options = new MqttConnectOptions()
if (config.password) {
options.setPassword(config.password as char[])
options.setUserName(config.user)
}
options.setCleanSession(false)
client.connect(options)
And my publish code
def message = new MqttMessage(Json.encode(outgoingMessage).getBytes())
try {
client?.connect(options)
def topic = client.getTopic('processMsg')
message.setQos(1)
def token = topic.publish(message)
if (client) {
client.disconnect()
}
Thanks
The Paho client will only persist in-flight messages when it is connected to the broker.
Typically, when connectivity issues start to arrive you'll see message timeouts popping up
Timed out waiting for a response from the server (32000)
At that point the message will still be persisted.
However, when the connection is lost, and you start seeing this
Client is not connected (32104)
You should assume that the message has not been persisted by Paho.
You can debug this in org.eclipse.paho.client.mqttv3.internal.ClientComms :
/**
* Sends a message to the broker if in connected state, but only waits for the message to be
* stored, before returning.
*/
public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException {
final String methodName = "sendNoWait";
if (isConnected() ||
(!isConnected() && message instanceof MqttConnect) ||
(isDisconnecting() && message instanceof MqttDisconnect)) {
this.internalSend(message, token);
} else {
//#TRACE 208=failed: not connected
log.fine(className, methodName, "208");
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
}
}
The internalSend will persist the message, but only if it is connected to the broker.
Also take into account that there is a maximum number of inflight messages that Paho can process. If it exceeds that it will also decide to not persist the message.
You could just setup a local broker and bridge that with the remote broker. That way you can queue up all your messages locally and when the remote broker comes back online all can be delivered.
Yes... After you get an exception that the message can't be delivered, it has to be either persisted or the message needs to be regenerated.
If you plan to use a local broker you can look at Really Small Message Broker (https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=d5bedadd-e46f-4c97-af89-22d65ffee070)
I have the same problem you had sometime ago:
"Getting errors with remote actor deployment in Akka 2.0 RC2"
How did yousolve it? I'm using Akka 2.1.2, but I think my problem is the concept.
I create the actor:
ActorRef actorOf = system.actorOf(new Props(HelloWorld.class), "injbct");
and then in other jvm I try to lookup it up :
ActorRef actorFor = system.actorFor("akka://KSystem#127.0.0.1:2552/user/injbct");
Regards, José
I have discovered that if you are accessing a remote actor in the same machine the localhost address of 127.0.0.1 or the machine's actual IP address must be used in both the configuration of the remote actor and the actor declaration in the actor user, i.e. they cannot be mixed.
The remote actor config
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "127.0.0.1"
port = 2554
Considering the machine to have an IP address of 1.2.3.4, then
This works
val workerRouter =
context.actorFor("akka://PrimeWorkerKernel#127.0.0.1:2554/user/PrimeWorkerActor")
This does not and results in a connection refused
val workerRouter =
context.actorFor("akka://PrimeWorkerKernel#1.2.3.4:2554/user/PrimeWorkerActor")
For starters, you should read the Akka Remoting documentation. Then, make sure you have the remoting dependency in your pom file:
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.10</artifactId>
<version>2.1.4</version>
</dependency>
Then, both sides (the calling side and the receiving side will need to have remoting config in their application.conf files similar to the example in the remoting docs:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "127.0.0.1"
port = 2552
}
}
}
You'll notice that the actor ref provider has been set to the RemoteActorRefProvider as opposed to the default LocalActorRefProvider. Without this on the sending actor side, things won't work. You'll also see that netty binding info has been setup and this is really important on the receiving actor side so that ActorSystem is listening for remote connections and messages. If you follow the steps in the docs and start up your receiving actor system as ActorSystem("KSystem") then things should work for you.
I'm using Netty with Java trying to configure a TCP client. Everything is working so far, except that I'm connecting on port 1050 but when I call messageEvent.getRemoteAddress() on messageReceived() method of the handler, I'm getting the port 1500. I changed the port to 1049 but I'm still receiving 1500. This is Netty's problem or can it be the server's problem?
My hardware setup here is: this netty client running on a Java server, and several access control equipments spread through the area here. The equipments act as tcp servers and the netty as the client, that process everything the server sends and just reply to them.
The tcp server initialization is this:
private ChannelFactory fabrica;
private ServerBootstrap bootstrap;
public void iniciarServidorTCP() {
fabrica = new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
bootstrap = new ServerBootstrap(fabrica);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoderDeMensagem", new MensagemDecoderTCP());
pipeline.addLast("handlerGerente", new GerenteTCP());
pipeline.addLast("encoder de mensagem", new MensagemEncoderTCP());
return pipeline;
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.reuseAddress", true);
bootstrap.bind(new InetSocketAddress(1050));
}
Any idea why I'm getting 1500 instead of 1050? Could it be a problem with the equipment?
Every TCP connection has a source port and a destination port. When you connect to a server, the server sees the destination port as its well-known address. The client picks the source port. On either end, getting the "remote address" gets the other side's address. So when you call get remote address on the server, you get the client's address, not the server's.
Imagine you have a server with one IP address and one well-known port. Now, say you have a client machine with one IP address. If it make's four connections to the server, how can either end tell those connections apart? The answer is that the client port is different.