keep all connected clients' ip in netty - sockets

My TCP server uses netty.The situation is: When a client connects to the server,I will save the client's ip in a global variable(such as a Map); When the client is disconnected,I will remove the IP from the map.
I used channelConnected() and channelDisconnected() method in SimpleChannelHandler.But my problem is ,some times the channelDisconnected() method cannot catch the event when I think the client is disconnected(maybe the computer closed,or the client process closed,or some other situations...) Can you give me some suggestions.

Just use DefaultChannelGroup which will automatically remove the Channel from it when it was closed.
Alternative you can register a ChannelFutureListener to the Channels close future to do the removal from your map.
Something like this:
channel.getCloseFuture().addListener(new ChannelFutureListener() {
public void operationCompleted(ChannelFuture f) {
map.remove(f.getChannel());
}
});

Related

Milo: get IP of client

Is there a way to get a Clients IP in Context of a write?
I want to get the IP of an Client that writes to my Milo-OPCUA-Server, so I can handle these writes differently based on the Clients IP (local Clients should be able to write directly on the Server, whilst other writes should get forwarded to another Server)
Okay, this is not part of any official API right now, so it almost certainly will break in the future, but:
With the OperationContext you get when implementing AttributeManager#write(WriteContext, List<WriteValue>):
context.getSession().ifPresent(session -> {
UaStackServer stackServer = context.getServer().getServer();
if (stackServer instanceof UaTcpStackServer) {
ServerSecureChannel secureChannel = ((UaTcpStackServer) stackServer)
.getSecureChannel(session.getSecureChannelId());
Channel channel = secureChannel.attr(UaTcpStackServer.BoundChannelKey).get();
SocketAddress remoteAddress = channel.remoteAddress();
}
});
I'll have to add some official API to do this, probably something hanging off the Session object.

How to get all established connections to vertx based http server?

I have got https server run by vertx http stack. Is there a way to get client IP addresses for all established connections (i.e. for audit, security, monitoring, QoS and other purposes). Ideally, I would like to have event (callback) driven API notifying about established and closed connections. How can I achieve it?
Current workaround is to poll a tool similar to netstat, but it is very inconvenient and not really real-time (i.e. short connections can be missed).
Github community responded with the answer: https://github.com/vert-x3/vertx-web/issues/685
You can use the HttpServer connection handler and manage it yourself easily:
server.connectionHandler(conn -> {
// Track conn.remoteAddress()
conn.closeHandler(v -> {
// Cleanup track of conn.remoteAddress()
});
});
In my example I'll write all IP addresses to a console. But of course you could write it to a log file, DB, or some other place of your choice.
Vertx vertx = Vertx.vertx();
// This your regular router
Router router = Router.router(vertx);
// We'll have two routes, just for fun
router.route("/a").handler((ctx) -> {
ctx.response().end("A");
});
router.route("/b").handler((ctx) -> {
ctx.response().end("B");
});
Router filterRouter = Router.router(vertx);
filterRouter.get().handler((ctx)->{
System.out.println("IP is " + ctx.request().remoteAddress());
// Forward to your actual router
ctx.next();
});
filterRouter.mountSubRouter("/", router);
vertx.createHttpServer().requestHandler(filterRouter::accept).listen(8080);

How do I call a method on my ServiceWorker from within my page?

I have a ServiceWorker registered on my page and want to pass some data to it so it can be stored in an IndexedDB and used later for network requests (it's an access token).
Is the correct thing just to use network requests and catch them on the SW side using fetch, or is there something more clever?
Note for future readers wondering similar things to me:
Setting properties on the SW registration object, e.g. setting self.registration.foo to a function within the service worker and doing the following in the page:
navigator.serviceWorker.getRegistration().then(function(reg) { reg.foo; })
Results in TypeError: reg.foo is not a function. I presume this is something to do with the lifecycle of a ServiceWorker meaning you can't modify it and expect those modification to be accessible in the future, so any interface with a SW likely has to be postMessage style, so perhaps just using fetch is the best way to go...?
So it turns out that you can't actually call a method within a SW from your app (due to lifecycle issues), so you have to use a postMessage API to pass serialized JSON messages around (so no passing callbacks etc).
You can send a message to the controlling SW with the following app code:
navigator.serviceWorker.controller.postMessage({'hello': 'world'})
Combined with the following in the SW code:
self.addEventListener('message', function (evt) {
console.log('postMessage received', evt.data);
})
Which results in the following in my SW's console:
postMessage received Object {hello: "world"}
So by passing in a message (JS object) which indicates the function and arguments I want to call my event listener can receive it and call the right function in the SW. To return a result to the app code you will need to also pass a port of a MessageChannel in to the SW and then respond via postMessage, for example in the app you'd create and send over a MessageChannel with the data:
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
console.log(event.data);
};
// This sends the message data as well as transferring messageChannel.port2 to the service worker.
// The service worker can then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
// See https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
and then you can respond via it in your Service Worker within the message handler:
evt.ports[0].postMessage({'hello': 'world'});
To pass data to your service worker, the above mentioned is a good way. But in case, if someone is still having a hard time implementing that, there is an other hack around for that,
1 - append your data to get parameter while you load service-worker (for eg., from sw.js -> sw.js?a=x&b=y&c=z)
2- Now in service worker, fetch those data using self.self.location.search.
Note, this will be beneficial only if the data you pass do not change for a particular client very often, other wise it will keep changing the loading url of service worker for that particular client and every time the client reloads or revisits, new service worker is installed.

Smack service discovery without login gives bad-request(400)

I am trying to discover items that a pubsub service provides. When I log into the target server, I can get the response successfully. But when I connect bu do not login, it gives a bad request error.
This is the code:
ConnectionConfiguration config = new ConnectionConfiguration(serverAddress, 5222);
config.setServiceName(serviceName);
connection = new XMPPConnection(config);
connection.connect();
connection.login(userName, password); //!!!when I remove this line, bad request error is received
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);
DiscoverItems items;
try {
items = discoManager.discoverItems("pubsubservice." + serverName);
} catch (XMPPException e) {
e.printStackTrace();
}
Is there a way to discover items when the user is not logged in, but the connection is established?
No, you must authenticate to send stanzas to any JID in XMPP (otherwise they would not be able to reply to you, since they wouldn't know your address).
Perhaps one option for you is anonymous authentication. Most servers support it, and it creates a temporary account on the server for you, with a temporary JID. You don't need a password, and login time is quick.
#MattJ is correct and you could try using anon login. That will get you part way there.
Your current request will only get you the nodes though, after which you will have to get the items for each node. It would be simpler to use PubsubManager to get the information you want since it provides convenience methods for accessing/using all things pubsub.
Try the documentation here, the getAffiliations() method is what you are looking for.
BTW, I believe the typical default service name for pubsub is pubsub not pubsubservice. At least this is the case for Openfire.

Any off the shelf app to rebroadcast tcp packets?

I am working with a 3rd party device which opens a tcp port that only allows one connection at a time. If my app connects to the port, all other connections are denied.
I'd like to find an app that basically connects to this port, then allows others to connect to it on a different port.
Any data sent out of the device's port is then rebroadcast to any connected client.
I know how to write such an app, but it seems like it would be something someone else has already thought off and written it & shared, and I could avoid taking the time to write it.
basicaly code would be:
1) start a tcp socket server, binding to TO_PORT (clients connect to this)
2) connect as a client to DEVICE_IP:DEVICE_PORT
3) when data is read into a buffer from DEVICE_IP:DEVICE_PORT, the buffer content is resent to each connected client.
4) everything else that makes it a stable, working program.
This is for windows, and I'd prefer it not require a java install.
My google skills have failed me.
Anyone know of such an app?
Not a complete solution for you, but might be interesting, though
http://www.codeproject.com/KB/IP/serversocket.aspx
http://www.codeproject.com/KB/IP/UniversalTCPSocketClass.aspx
Guess I'll answer my own question.
I implemented the solution my self.
Key points to my solution:
A class named IPClient which wraps up a TcpClient instance, uses async model of calling TcpClient.BeginConnect, BeginRead, etc. It has a Timer used for reconnecting if it loses connection.
This is the class that connects to the device.
It's public interface would look something like this:
public class IPClient{
public event EventHandler<MyConnectedArgs> Connected;
public event EventHandler<MyDisconnectedArgs>Disconnected;
public event EventHandler<MyDataReceivedArgs> DataReceived;
public bool Connect(string address, int port){...}
public bool Disconnect() {...}
}
To open the port that would allow other clients to connect, I used this library: http://codeproject.com/KB/IP/BasicTcpServer.aspx and modified it a bit.
It's job was to open a port, accept connections, and do the following:
in the Connected handler, start the listening port
in the Disconnected handler, stop the listening port
in the DataReceived handler, broadcast the data to any connected clients.
I'll leave out the rest of the boring details, but say it wasn't "too hard", and eventually I just had to roll my own.
command line usage: myapp.exe remote_addr remote_port listen_port
psuedocode/main idea of my program main:
static int Main(string[] args){
//SetConsoleCtrlHandler(my callback re: ctrl+C,etc)
//get command line params
var ipClient = new IPClient();
var myprovider = MyTcpServiceProvider();
var server = new TcpServer(myProvider, listenPort);
ipClient.Connected += (sender, e) => server.Start();
ipClient.Disconnected += (sender,e) => server.Stop();
ipClient.DataReceived += (sender,e)=> provider.BroadcastToClients(e.Data);
ipClient.Connect(remoteAddress, remotePort);
//wait for Ctrl+C or program exit
//shutdown code,etc
return 0;
}