
In this article I want to talk about my implementation of the pattern called
Long Polling for the
Nancy framework. The code of my module is more than four years old, during which he has successfully worked in a number of projects on ASP .Net MVC. This week I decided to design it in the form of a Nancy module and put it on the githab for the common good, since I could not find a similar solution.
With the help of my module in just a minute and a couple of simple actions you can get a reliable feedback channel from the server to the browser. Want to know more?
A bit of background
While you come to your senses after viewing the picture to attract attention, I will briefly tell you where this module of mine came from. At the end of 2011, in one of the projects we needed to organize a real-time communication channel from the server to the web interface. At that time I was familiar with the technique of solving such a problem, since there were already several articles on Comet, Server Push and Long Polling. Moreover, according to my feelings, it was during this period that the fashion came to web interfaces and web applications that update the data in real time without reloading the entire page in the conditions of the web.
I decided not to reinvent the wheel and find a ready-made solution. Our project was implemented on ASP .Net MVC 3 (or another 2?), So the obvious choice was SignalR, which miraculously was released just a month before we needed it. SignalR connected and worked, at first glance, without problems, but after a while it turned out some annoying features related to detecting loss of connection with the server and reconnecting. I have no doubt that SignalR is doing well now, but at that time these “features” were annoying, as a result of which the natural desire to do everything from scratch with blackjack and boats overcame the desire to figure out what was wrong with someone else's code. During the evening, I wrote a controller and a client script that existed almost unchanged until today, roaming from one project to another.
')
A year ago, we and our team discovered an excellent alternative to the Microsoft MVC “in the face” of the
Nancy framework. Since now we are doing new projects on it, our old developments have slowly begun to take shape in the form of Nancy modules. As a result, the turn came to the long polling module, which turned out, in my opinion, self-sufficient and decent enough to share it with the community. Download the code with an example and tests
here .
Now, when I, I hope, justified my cycling, let's get to the point.
Instructions for use
In order to run the module in your project, you will need:
- download and bring down the Nancy.LongPoll project, connect it to your project;
- register the PollService class as a singleton in the IoC container of the application;
- on each web page where you want to receive notifications from the server, call startPoll (), and also override the pollEvent function (messageName, stringData), where stringData will come as a string. Usually, it is most convenient to send json in this line, then do JSON.parse (stringData).
After you complete these steps, everything will be ready in order to start sending messages from the server to browsers. To send messages to the PollService class, several methods are provided that are provided to the IPollService interface:
interface IPollService { // void SendMessage(List<string> clientIds, string messageName, string message); // void SendMessage(string clientId, string messageName, string message); // void SendMessageToAllClients(string messageName, string message); // sessId void SendMessageToSession(string sessId, string messageName, string message); // sessIds void SendMessageToSessions(List<string> sessIds, string messageName, string message); // void StopClient(string clientId); }
Immediately explain the terminology. A
client is any window or browser tab connected to the service. The client is the minimum unit to which you can send a personal message. Clients are characterized by string identifiers that are created when the connection is established, stored on the server and sent with each request from the browser. The identifier string is generated randomly and is long enough so that the identifier cannot be falsified.
Session - corresponds to the concept of a session generally accepted in web technologies. The session ID is stored by default in a cookie named
nancy_long_poll_session_id . When you send a message to a session, you are most likely sending it to a specific browser, i.e. All browser tabs will receive this message at once. You can override the server-side session identifier generation by implementing the ISessionProvider interface and registering your class in the IoC container of the application or request. You may need to redefine a session if you already have, for example, linking users to sessions. This way you can send messages to specific users. If you do not do this, the default implementation will be used, which generates a session identifier as a random string.
In addition, you can implement the ILogger interface to receive diagnostic messages from the module. The default implementation is the EmptyLogger implementation, which does nothing.
And finally, at any time you can stop the survey initiated by the client, using the JavaScipt call stopPoll (), although this is usually not required. You can resume polling by calling startPoll () again.
Module usage example
So that you could try all this in action, I wrote a small example of using the module, which is available at the link: https://github.com/AIexandr/Nancy.LongPoll/tree/master/Nancy.LongPoll.Example

The example is implemented as a self host console application that runs on port 80m, so for it to work, you may need to run Visual Studio with administrator rights. In addition, do not forget that the port can be busy, for example, Skype. The server once a second sends to all clients the value of the incremented counter, which is displayed in the browser window. You can open several windows at once to make sure that the counter is updated synchronously.
At the top of the application page displays the status of communication with the server. By pressing the Stop poll button you can terminate the connection at the initiative of the client. You can resume the connection by pressing the Start poll button. Using the Stop notifications on server button, you can pause the work of the sample server notifications distribution module. Button Start notifications on server allows you to resume its work.
Using the chrome debug toolbar, you can see how the polling module works (see screenshot):

- After loading the page there was a client registration.
- About once a second there are notifications from the server.
- The Stop notifications on server button was pressed. A POST request was sent to the server, which turned off the demo notification generation service. Sending notifications by the server stopped for 2.8 minutes. hung a long HTTP request to the server (see the long horizontal green bar in the screenshot).
- The Start notifications on server button was pressed, a POST request was sent to the server, which resumed the operation of the demo service, and then immediately once again, messages began to arrive once a second.
- The Stop poll button was clicked. The survey stopped at the initiative of the client.
- The Start poll button was clicked. The survey resumed.
The example does not show the case of disconnecting clients at the initiative of the server. You can try to master this function yourself; StopClient () method of the polling service is responsible for it.
Example project structure:
- Program.cs is the standard class for the console application. Launches Nancy Self Host and opens two browser windows.
- Bootstrapper.cs - overrides the standard Nancy bootstrapper. Registers in the IoC container a polling service and a demonstration notification generation service.
- ExampleModule.cs - demo NancyModule. Returns Index.html at the request of the browser and responds to POST / Start and / Stop requests that start and stop the demo notification service.
- ExampleNotificationService.cs is a demonstration notification service. Once a second, it sends a “broadcasting” (to all customers) notification to the polling service with a new value of the incremented counter.
- Index.html is the actual web interface page. Located in a project as Embedded Resource.
Module device
The Nancy.LongPoll.dll library includes server-side .Net classes and a client poll.js script built into the library as an Embedded Resource. Here is a list of the library files:
- ContentModule.cs is a Nancy module that is responsible for issuing embedded resources for HTTP requests. As part of Nancy.LongPoll.dll is used to issue poll.js.
- Logger.cs - contains the interface and an empty logger implementation. Allows you to untie Nancy.LongPoll from a specific implementation of the logger.
- poll.js is a poll script containing the implementation of client polling logic.
- DefaultSessionProvider.cs - contains the interface and the default implementation of the session id provider.
- PollModule.cs - Rationalization of the Nancy module, necessary for the operation of the polling service.
- PollService.cs is the actual polling service.
At first, I wanted to describe in detail the structure and operation of the module, but then I decided not to overload the article with unnecessary details. Instead, I’ll give the most significant facts about poll.js and the PollService class:
- The PollService class contains a list of currently connected clients linked to client and session IDs. Clients are described by the PollService.Client class.
- Sending messages from the server to clients is done in the form of a json structure. The message structure is described by the PollService.Message class and contains the fields: sign of success of the action, return code, message name, data line. Of course, it would be more correct to transfer the success of the action and the return code to the HTTP status codes, but I do not consider this to be such a big lack of implementation.
- The poll.js and PollService interaction begins with registering a client. During the registration process, the client is assigned an identifier, a message queue, and seqNumber is the message number processed by the client.
- poll.js opens a “long” connection to the PollService, reporting the number of the last message received. In the case where there is a message with the number greater than the last processed by the client for the client, the message is retrieved from the queue and transmitted as a response to the client, and then deleted from the queue. If there are no messages, PollService does not release the HTTP connection and “time pulls”. There is something to improve: if several messages have accumulated in the message queue since the client last accessed, they will be sent to the client one at a time, although it would be more correct to give the client an array of messages at once, then parse it in poll.js.
- PollService remembers the time of the last call of each client. If the last client passes more than the time specified in the PollService.CLIENT_TIMEOUT field, the client is considered disabled. Again, there is something to improve. The client does not notify the server of its intention to disconnect, even if you call stopPoll ().
- The number of concurrently connected clients can be limited by the value of the PollService.MAX_CLIENTS field.
- poll.js after calling startPoll () begins active attempts to contact the server and register. If the connection is lost or an error occurs during data transmission, poll.js automatically resumes connection attempts. The status of the script activity is in the variable isPollActive, the state of communication is reflected in the variable isPollConnected. However, if you call the PollService.StopClient (clientId) method on the server, poll.js will stop trying to establish a connection until the next startPoll () call. As a further improvement of the module, you can add versions of the method for stopping the connection of the session and, in general, all clients.
- The PollService class is designed and implemented with respect to the multi-threading entity of the processes occurring here. In my opinion, it turned out quite well, but I used simple lockes, although it would be nice to redo it on ReaderWriterLockSlim.
That is, in fact, everything that I wanted to tell. Feel free to ask questions and use my module. I would also be happy to consider smart pull requests.