I am building a server client application using netty and ios, I am facing a problem when the user just turns off WiFi on his/her ios device, the netty server does not know about it.
The server needs to know to do cleanup for that user and set him/her offline, but now when the user tries to connect again, the server just tells him that he/she is already online.
If I understood your problem correctly: You want to listen for client channel closed events in server side and do some session cleanup,
There are two ways to listen for channel closed events in Netty :
1) If your server handler extends SimpleChannelHandler/SimpleChannelHandler, then you can override following method and write your session cleanup logic there
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception;
2) If you have only access to the channel reference, then you can get the channel close future and register your implementation of ChannelFutureListener with your session cleanup logic,
ChannelFuture closeFuture = channel.closeFuture();
closeFuture.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
//session cleanup logic
}
});
Use IdleStateHandler
You can detect when there is no request/responses in given time intervals.
well the question is 9 years old a lot has happened since then
2008 Economic Collapse, Wars, Coronavirus.
On the bright side small Stock-Retailers seem to be winning the war against Hedge-funds on Wall-Street.
Back to the Question:
We were facing a similar problem where we had to provide a log message on the server-side every-time a client would close the channel.
The solutions provided above did not help but after 9 years a lot might have also changed in Netty.
Instead we extended our handler with the "MessagetoMessageDecoder" which also extends the ChannelInboundHandlerAdapter.
We then override the void method "channelUnregistered" which triggers when a channel is unregistered from its EventLoop.
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelUnregistered();
//Your logic goes here...
}
This also works when the server itself closes the channel.
Hope this helps somebody.
Check session id and allow renegotiation. Or you may use something like cookie controller.
May I ask off topic question: How does your client on ios interact with netty server? (what framework you use on client side, and what decoder/encoder use? )
Related
Error message in Application Insights:
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. (OurApiUrlAddress:443) A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
It's always a 21 seconds TCP timeout, this is a very generic error I know, but the reason for this error is not always the same, I've been reading all the threads about this. We've been investigating this problem for months with no luck, we're also in contact with Azure team.
Important: this same site written in RUBY was using this same API without any problem in the past, the API is responsive and it's called from other sites without any problem, but this specific site was migrated from RUBY to .NET and at the same time this site was hosted in AZURE, this are the 2 big changes. This just happens when the site (remember it's hosted in Azure) calls to API / services hosted in our company, this doesn't happen when site calls a service hosted somewhere else, these makes us think the problem may be related to the company infrastructure but it can't be that alone, this has to be related to .NET and AZURE someway since these APIs and services respond perfectly to calls from other sites hosted in our network and they were working fine with the ruby version of this site. These Apis and services are not throwing this error when called in-browser from outside the company network.
The services/apis are behind a firewall but ports are perfectly configured (there are not any other traffic apps nor devices at play).
This error doesn't seem to be related to port exhaustion or SNAT, since sometimes only 1 developer alone is working in the DEV environment and he gets this socket exception error.
Just to give an idea we're getting around 250 socket exceptions a day on production, and this is just a small percentage of all the calls, so there is something that, just sometimes, is making this happen.
We know about the well known HttpClient issue when multiple instances are created, so we decided to use the Singleton approach ensuring only 1 instance per API/Service, as I'll show here, this is the call that gives more socket exceptions:
In StartUp class/file:
services.AddSingleton<IUploadApi>(new UploadApi(new HttpClient() { BaseAddress = new Uri(appSettings.Endpoints.UploadServicesUrl) }));
Part of appsettings.json:
"Endpoints": {
"UploadServicesUrl": "https://ourApiUrlAddress"
},
UploadApi.cs
public interface IUploadApi
{
Task<UploadArtworkViewModel.UploadConfigurationData> GetUploadConfiguration();
}
public class UploadApi : IUploadApi
{
private readonly HttpClient httpClient;
public UploadApi(HttpClient client)
{
httpClient = client;
}
public async Task<UploadArtworkViewModel.UploadConfigurationData> GetUploadConfiguration()
{
var response = await httpClient.GetAsync("api/GetUploadConfiguration").ConfigureAwait(false);
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject<UploadArtworkViewModel.UploadConfigurationData>(json);
}
}
Call from controller:
model.UploadConfiguration = await UploadApi.GetUploadConfiguration().ConfigureAwait(false);
Any idea on things to test or places to look are welcome, obviously I've not been able to reproduce this one. We know there's always a 21 seconds timeout, that's a TCP timeout, but that doesn't help much. Maybe for some reason the connection is dropped or Azure is having problems (sometimes) when accessing the company network. I can post more info from application insights if needed but I don't see anything special there about the error.
EDIT - More info: It happens when any API or service is called from this MVC site Controllers, so the problem appears sporadically (still like 300 times per day) when the site server tries to reach an API or service, this makes me believe it's something related to the company infraestructure, but still no idea what it could be.
From asp.net monsters:
"the application has exited and yet there are still a bunch of these
connections open"
"They are in the TIME_WAIT state which means that the connection has
been closed on one side (ours) but we’re still waiting to see if any
additional packets come in on it because they might have been delayed
on the network somewhere."
Even if you're using a singleton HttpClient, it seems that some of the connections are awaiting for additional packages which leads to socket exaustion.
The solution is to change your code and use HttpClientFactory or HttpClientFacotoryLite. The reason to use HttpClientFactory is that produces HttpClient instances that resuse Socket handlers from a pool of socket handlers. The handlers are recycled periodically to also take care of DNS changes. In summary, when using HttpClientFactory, HttpClient delegates work to a SocketClientHandler.
We finally got this problem fixed after working together with Azure team for some time, it was a gateway problem, solution was applying NAT/Vnet Integration. This is what we did to fix it:
https://learn.microsoft.com/en-us/azure/app-service/networking/nat-gateway-integration
I am using the HiveMQ MQTT client in Spring to receive MQTT messages.
My client configuration looks like this
public Mqtt3AsyncClient mqtt3Client() {
var mqtt3Client = Mqtt3Client.builder()
.serverHost("my.host")
.sslWithDefaultConfig()
.serverPort(0000)
.automaticReconnectWithDefaultConfig()
.buildBlocking();
mqtt3Client.connect();
return mqtt3Client.toAsync();
}
After the client is available, another Spring Bean is initialized using the client. It subscribes a topic:
#PostConstruct
public void subscribeTopic() {
mqtt3AsyncClient.subscribeWith()
.topicFilter("topicfilter")
.qos(MqttQos.AT_LEAST_ONCE)
.callback(message -> {
/*Handle message*/
})
.send()
.whenComplete((mqtt3SubAck, throwable) -> {
if (throwable != null) {
/*Logging*/
} else {
/*Logging*/
}
});
}
I saw multiple times that no more messages were delivered to my application while I was still able to use the client connection to send messages (thus it was connected at that time).
I could not find any documentation on how the HiveMQ MQTT client handles the configured automaticReconnectWithDefaultConfig(). Can anyone point out, whether my subscription created in subscribeTopic() is resubscribed?
I also found the method addSubscription() that may replace the .topicFilter(..).qos(...) part. I also could no find any information whether this makes the subscription more resilient to connection losses.
I'd appreciate any kind of information on that topic.
Thanks.
Currently the HiveMQ MQTT Client will only continue to receive messages for subscriptions if the broker reports an existing session in the ConAck of the re-connect. This requires two things - 1) you need to set cleanSession = false when initially connecting, and 2) the broker needs to have not lost the session in between connections.
For 1) you can try adding this to your connect:
client.connectWith().cleanSession(false).send();
With 2) it will depend on the broker and what the cause of the connection loss was. If it was 'just' a network outage and the broker was running normal in the background then it should work fine. If the broker crashed and was restarted, then it will require that the broker has persistence configured and that it was able to re-establish the session after the restart.
There are actually a couple of discussions over on the github project page of the HiveMQ MQTT Client regarding this issue and whether functionality should be added to auto re-subscribe even in the case that no pre-existing session was found. And also on a related note whether any publishes done while the connection was lost should auto-publish even if no session was found after the re-connect. If these are features you require, maybe hop on over there and chime in on the discussions :)
Lastly, you can also manually perform re-subscribes by adding a MqttClientConnectedListener while building the client which can then re-create the subscriptions each time the auto re-connect happens.
HTH
Cheers,
C
I have a question, how can i get userId from Application controller? I saw many samples, how to get it from Hubs. But i don't know how can i call hub from controller in other way then
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
in the context i havent any id.
I saw samples like that, here i can call context and get UserId, but this works only in the Hub.
var name = context.User.Identity.Name;
I know that i can do something like that:
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
But i have to call hub from the controller.
Thanks for help
May I suggest a slightly different approach. In general that poking into the the hub from outside the signalr context doesn't work well or at least makes things more complicated.
You could have the controller act as client to the hub itself and expose the information you need via the hub to the controller. All you'd need is the SignalR Desktop Client package. While it adds an overhead, you'll have a much more compliant way for data retreivement and as a benefit a nice separation of concerns.
Here's a similar question that I replied to. HTH.
If you have some kind of authentication in your application, a good idea might be keeping a mapping between users and connections.
This way, whenever you want to send a message to a user, simply retrieve all that user's connectin IDs and send the message to all of them.
foreach(var connectionId in UserMapping)
context.Clints.Client(connectionId).sendMessage(message);
This way, you are able to send messages to specific clients from outside the hub and you are sure that all instances of the client get notified.
Take a look here for more information on Mapping SignalR Users to Connections.
Hope this helps.
Best of luck!
i found a solution.
By the way, thanks for your answers.
I try to use signalR from class which was calling by the application controller.
In the class i haven't got any context and special data about user who called server.
But i found into the controller info about which user calling the server.
this.User.Identity.Name
Happy coding ;)
I have got an ASP.NET MVC 4 application, and I want it to send a report e-mail every week. I've read about Quartz.NET, but it's too powerful for this easy task. Now I'm trying to use NCron, but it requires an initialiser in the Main() method (with obligatory parameter args):
class Program
{
static void Main(string[] args)
{
Bootstrap.Init(args, ServiceSetup);
}
}
Is there the way to do this in the Application_Start()? What should I pass as a args param? What other solutions can solve this task?
Author of NCron speaking…
First: I have never myself integrated NCron into a web application, and I am not sure how well it will work. For instance, as Kenneth points out, IIS will shut down your app if it does not receive any traffic, and there might be other hiccups as well.
In order to integrate NCron into a web app, I suggest that you ignore Bootstrap.Init() (designed specifically as an entry point to console apps) and rather work directly with SchedulingService:
using (var service = new SchedulingService())
{
service.Hourly().Run<DataUpdateJob>();
service.Daily().Run<RecycleCacheJob>();
service.Start();
}
Again: I have never done this myself, but please do give it a try, and let me and everyone else know how you fare.
You'll have to look up what ncrone does with those parameters. What this does is pass the command-line arguments of your windows app to the component.
If you're using it on a web app, you don't have command-line args so if it needs arguments, you will have to construct the arguments yourself (either hard-coded or from a config-file or a database or ...)
It's also possible that these are optional, then you can just pass in an empty array (but again, check the docs of ncrone)
Also, keep in mind that when your application shuts down (standard that is after 20 minutes without any activity), your cron runner will not wake it up. If that will be the case you either need to keep the application alive by assuring that at least one request is done every 20 minutes or configure IIS to keep it alive always.
I'm aware of the Chris Fulstow project log4net.signalr, it is a great idea if you want a non production log since it logs all messages from all requests. I would like to have something that discriminates log messages by the request originating them and sed back to the proper browser.
Here what I've done in the appender:
public class SignalRHubAppender:AppenderSkeleton
{
protected override void Append(log4net.Core.LoggingEvent loggingEvent)
{
if (HttpContext.Current != null)
{
var cookie = HttpContext.Current.Request.Cookies["log-id"];
if (null != cookie)
{
var formattedEvent = RenderLoggingEvent(loggingEvent);
var context = GlobalHost.ConnectionManager.GetHubContext<Log4NetHub>();
context.Clients[cookie.Value].onLog(new { Message = formattedEvent, Event = loggingEvent });
}
}
}
}
I'm trying to attach the session id to a cookie, but this does not work on the same machine because the cookie is overwritten.
here is the code I use on the client to attach the event:
//start hubs
$.connection.hub.start()
.done(function () {
console.log("hub subsystem running...");
console.log("hub connection id=" + $.connection.hub.id);
$.cookie("log-id", $.connection.hub.id);
log4netHub.listen();
});
As a result, just the last page connected shows the log messages. I would like to know if there is some strategies to have the current connection id from the browser which originate the current request, if there is any.
Also I'm interested to know if there is better design to achieve a per browser logging.
EDIT
I could made a convention name based cookie ( like log-id-someguid ), but I wonder if there is something smarter.
BOUNTY
I decided to start a bounty on that question, and I would additionally ask about the architecture, in order to see if my strategy makes sense or not.
My doubt is, I'm using the hub in a single "direction" from server to client, and I use it to log activities not originating from calls to the hub, but from other requests ( potentially requests raised on other hubs ), is that a correct approach, having as a goal a browser visible log4net appender?
The idea about how to correctly target the right browser instance/tab, even when multiple tabs are open on the same SPA, is to differentiate them through the Url. One possible way to implement that is to redirect them at the first access from http://foo.com to http://foo.com/hhd83hd8hd8dh3, randomly generated each time. That url rewriting could be done in other ways too, but it's just a way to illustrate the problem. This way the appender will be able to inspect the originating Url, and from the Url through some mapping you keep server side you can identify the right SignalR ConnectionId. The implementation details may vary, but the basic idea is this one. Tracking some more info available in the HttpContext since the first connection you could also put in place additional strategies in order to prevent any hijacking.
About your architecture, I can tell you that this is exactly the way I used it in ElmahR. I have messages originating from outside the notification hub (errors posted from other web apps), and I do a broadcast to all clients connected to that hub (and subscribing certain groups): it works fine.
I'm not an authoritative source, but I also guess that such an architecture is ok, even with multiple hubs, because hubs at the end of the day are just an abstraction over a (one) persistent connection which allows you to group messaging by contexts. Behind the scenes (I'm simplifying) you have just a persistent connection with messages going back and forth, so whatever hub structure you define on top of it (which is there just to help you organizing things) you still insist on that connection, so you cannot do any harm.
SignalR is good on doing 2 things: massive broadcast (Clients), and one-to-one communication (Caller). As long as you do not try to do weird things like building keeping server-side references to specific callers, you should be ok, whatever number of Hubs, and interactions among them, you have.
These are my conclusions, coming from the field. Maybe you can twit #dfowler about this question and see if he has (much) more authoritative guidelines.