Use jquery Ajax and. Net IHttpAsyncHandler to implement real-time website prompts and ajax instant messaging
After the project has been completed for a while, I have always wanted to write a blog to summarize it. I have never written a quality blog before. first, I was afraid that I could write a joke by everyone. Second, I was afraid that I could mislead others only by learning a little bit about it. So I have never written a blog, however, many Daniel encouraged programmers to write blogs. They can review the key points of their projects and discover many problems they did not find before. so try writing it yourself. I have never been used to summing up and want to change it. poor writing and lack of experience.
----------------------------------------------------- Split line -----------------------------------------------
Because of project requirements, the supervisor asked me to provide an instant reminder function after login, that is, to notify the user immediately when data changes. then I started searching for various keywords on Baidu and Google. there are several ways to achieve this. that is, Round Robin and persistent connections. in addition, there is an open-source framework signalr provided by Microsoft (which I know now ).
Because HTTP is stateless and has no connectivity. as a result, the data transmission between the web program and the server can only be: the browser sends a request to the server, the server then responds to the request, and then returns the data to be requested. that is, the relationship between the browser and the server is the relationship between the request and the response. The benefits of this relationship will not be mentioned (I do not know much --!), However, the server cannot actively send data to the browser because it is stateless. What if there is such a requirement? There are a lot of smart people, and there are also many ways for smart people to come up with solutions. Let's start to try out which solution is most suitable for project requirements.
1. signalr
There have been an article about signalr in the garden: The SignalR project was written by Dr. Zhang Shanyou.
I used SignalR in Asp. NET MVC to implement the push function. This article learned how to use it without in-depth research. It is suitable for instant web chat.
The main project is to implement a function similar to the database monitoring function, so don't consider this method. If you are interested, please take a look.
2. Round Robin
The so-called round-robin means that the client keeps sending asynchronous requests to the server and notifies the browser to process the requests when the database changes. this method is easy to implement, but I also know that it is difficult for the server to send requests without stopping services. If too many webpages are opened at the same time, the server may crash.
3. persistent connection
The first two methods are not what LZ wants. It seems that LZ can only sacrifice that trick: persistent connection.
Baidu and GOOGLE, the author of this article, explained the persistent connection in the words of a netizen: the client sends a request to the server, and the server receives the request and hlevels the connection, the client is returned only when data or request times out, and then the client sends another request until the page is closed, which also explains why it is called a persistent connection. for example:
In this figure, the first two request timeouts are set to 1 minute, and a request is sent immediately after the request is returned.
Well, since there is only one last trick left, you can play with the last one,
First, the client needs to send an asynchronous request:
/* Asynchronous request sent by the client */function asyncRequest () {$. ajax ({type: "POST", url: "asyncResult. asyn ", data:" time = 60 ", // request timeout success: function (data) {if (data! = "") {/* Execute the operation, for example, the pop-up prompt */} asyncRequest (); // send a request after the server responds}, error: function () {asyncRequest (); // send another request after the server throws an error }});}
The method used by the server to receive this asynchronous request must also implement asynchronous operations. Otherwise, normal requests will be blocked. Therefore, the IHttpAsyncHandler interface must be implemented to Implement Asynchronous computing on the server.
Public class asyncResponse: IHttpAsyncHandler {public token BeginProcessRequest (HttpContext context, AsyncCallback cb, object extraData) {signature result = new myAsyncResult (context, cb, extraData); asyncRequestMgr. add (result); asyncRequestMgr. send (); return result;} public void EndProcessRequest (IAsyncResult result) {asyncRequestMgr. resultStr = ""; // clear the result when the asynchronous end} public bool IsReusable {get {return false ;}} public void ProcessRequest (HttpContext context ){}}
The asyncResponse class is used to receive all asynchronous requests and hand them over to the static class asyncRequestMgr based on the request calculation result:
Public static class asyncRequestMgr {public static string resultStr = ""; private static myAsyncResult asyncResult; /// <summary> /// save an asynchronous request object to a static object for Operation // </summary> /// <param name = "result"> </param> public static void add (myAsyncResult result) {asyncResult = result ;}/// <summary> /// </summary> public static void send () {string time = asyncResult. contex. request. form ["time"]; getResult (time); asyncResult. send (resultStr ); // send data to the client} // <summary> // get the result or return a null value // </summary> private static void getResult (string time) {int I = int. parse (time), temp = 0; while (temp <I) {Thread. sleep (1000); // This class inherits from IHttpAsyncHandler and is used to extract a thread from the thread pool to execute this class. Therefore, let the thread Sleep (1000) it does not affect the UI thread/** query the database here, save the data to the variable resultStr, and then break out of the loop, */temp ++;
}}}
Then, the result is sent by the myAsyncResult class:
Public class myAsyncResult: IAsyncResult {public HttpContext contex; public AsyncCallback cb; public object extraData; /// <summary> /// initialize data /// </summary> /// <param name = "contex"> </param> /// <param name = "cb"> </param> // <param name = "extraData"> </param> public myAsyncResult (HttpContext contex, asyncCallback cb, object extraData) {this. contex = contex; this. cb = cb; this. extraData = extraData;} // <summary> // return the data requested by the client // </summary> public void send (string resultStr) {this. contex. response. write (resultStr );}}
Even if such an asynchronous request is completed, it can also monitor the database. But what if the customer accidentally refresh the database when querying the database in the background? In this way, the established connection will be disconnected, and since my front-end starts asynchronous requests during page loading, the refresh will send another request, the first query in the background continues. in this way, two requests are executed in the background and the database is queried together. if the database changes are queried by the first request, but the connection is disconnected because the customer refresh the page, the user will not be notified of the data changes. in addition, if the user does not accidentally press F5, the front-end will refresh the request and query the database at the same time. if 10 other users press F5 at the same time, it means 10 * N requests can be used to query the database at the same time. In the end, the server can only be overwhelmed and crashed. What if so? Because LZ usually does not look at MSDN much, it is really worried for a while, and finally suddenly found HttpContext. response has an attribute IsClientConnected, which helps a lot. It returns a BOOL value, indicating whether the current request is in the connection status. With this attribute, you can add a judgment in the getResult method. If IsClientConnected = false, an exception is thrown immediately and the query result is saved to the resultStr variable, in this way, the thread will not continue to execute.
The modified getResult method is as follows:
/// <Summary> /// get the result or return a null value // </summary> private static void getResult (string time) {int I = int. parse (time), temp = 0; try {
While (temp <I)
{
If (! AsyncResult. contex. response. isClientConnected) throw new Exception (); Thread. sleep (1000); // This class inherits from IHttpAsyncHandler and is used to extract a thread from the thread pool to execute this class. Therefore, let the thread Sleep (1000) it does not affect the UI thread. ** query the database here, save the data to the variable resultStr, and then break out of the loop ,*/
Temp ++ ;}} catch (Exception) {/* Save the result in the abnormal thread to resultStr */throw ;}}
Then, before executing the send method, determine whether the resultStr is empty. If it is not empty, you do not need to query the database and directly send the resultStr:
/// <Summary> ///// </summary> public static void send () {if (resultStr = ") {string time = asyncResult. contex. request. form ["time"]; getResult (time);} asyncResult. send (resultStr); // send data to the client}
In this way, no matter how long it takes to press F5, the server will throw an exception as long as it determines which request's connection status is false. It will keep up to one request to query the database. Now, even if there is no () i'm not afraid to press F5!
---------------------------------------- Split line -------------------------------------
For the first time, I think it is a technical post. If you think I have any mistakes, please point them out in time to avoid misleading others.