Build a web server of your own-implement Session

Source: Internet
Author: User

The last time we implemented a simple web server version that can implement some basic functions, we also mentioned that this version does not support session and cannot implement real dynamic interaction, this time, we will complete this function.

I. Session implementation principle

Anyone who has been engaged in web development knows that in most cases, the browser requests the server to use http requests, while the http requests are stateless. That is to say, each request server creates a new connection, when a response is received, the connection is closed. Although http1.1 supports keep-alive, it is most useful to avoid re-establishing the connection each time, instead of solving users' online status and other business needs. If the server wants to know the status of the client or identify the client, it cannot be achieved through the connection itself like a persistent connection, but it must be determined by the data in each request.
Let's take a look:

We can clearly see how the session is implemented. Generally, during the first request from the client, the server will generate a session_id (different servers may have different names and their values are a unique string) the server generates a session object to store session-related data. In response, you can use Set-Cookie to add session_id to cookies on the client. In subsequent access, each server checks whether session _ exists and can find the corresponding session object to identify the client.
There is another problem here: what if the client is closed? How does the server know? In fact, the server does not need to worry about whether the client fails. The common practice is to set the expiration time for the session, and reset the expiration time for each request. If there is no request before the expiration, the session will be cleared, in this way, the session ends. Note that the client session_id must be a temporary cookie. In this way, session_id is cleared when the browser is closed, otherwise, you can re-open the browser within the expiration time and continue to change the session, which is obviously unreasonable (this version does not consider this issue ).

Ii. Functional Design

As before, let's first design how to implement it in our project. First, let's determine the data structure. The session itself does not have to be said. The core is a map that stores data. At the same time, we also need to record the last access time of each session to handle the expiration problem.
So how do we store the session set? We all know that every web program starts to generate some built-in objects. sessions are equivalent to sessions (within a session), so there is also a web application level, global access to the web application. Because the session set needs to be accessed at multiple layers of the application, we need to implement a single-instance ApplicationContext to process global data and simultaneously process session creation and access.
Next, let's design how to deal with the session. First, based on the above introduction, we should judge and generate a session after receiving the request to ensure that the subsequent business can obtain the session. Therefore, we should () method. In addition, since the previously designed controller is called, we only pass a map parameter set, so the session cannot be obtained in the controller, therefore, before calling the controller, we put the session into the map (this is only a simple practice. It is better to encapsulate the parameters, so if you need to expand the parameter type in the future, you only need to modify the encapsulated class ).
Then we implement a scheduled task to regularly clean up expired sessions.

Iii. Implementation Code

With clear thinking, code implementation is very simple. I will not detail each part of the code here. I will understand it after reading the annotations.
First, let's take a look at the Session and ApplicationContext code (in other words, no one proposed @ sweet potato to add a code folding function ):

/*** Session data ** @ author guojing * @ date 2014-3-17 */public class HttpSession {Map <String, Object> map = new HashMap <String, Object> (); date lastVisitTime = new Date (); // last access time public void addAttribute (String name, Object value) {map. put (name, value);} public Object getAttribute (String name) {return map. get (name) ;}public Map <String, Object> getAllAttribute () {return map;} public Set <String> getAllNames () {return map. keySet ();} public boolean containsName (String name) {return map. containsKey (name);} public Map <String, Object> getMap () {return map;} public void setMap (Map <String, Object> map) {this. map = map;} public Date getLastVisitTime () {return lastVisitTime;} public void setLastVisitTime (Date lastVisitTime) {this. lastVisitTime = lastVisitTime ;}/ *** global data and session-related data, Singleton * @ author guojing * @ date 2014-3-17 */public class ApplicationContext {private Map <String, object> appMap = new HashMap <String, Object> (); // ApplicationContext global data/*** it's hard to figure out whether sessionMap is necessary to consider thread security, please also advise */private ConcurrentMap <String, HttpSession> sessionMap = new ConcurrentHashMap <String, HttpSession> (); // session data private ApplicationContext () {}/*** internal class implementation Singleton */private static class ApplicationContextHolder {private static ApplicationContext instance = new ApplicationContext ();} public static ApplicationContext getApplicationContext () {return ApplicationContextHolder. instance;} public void addAttribute (String name, Object value) {ApplicationContextHolder. instance. appMap. put (name, value);} public Object getAttribute (String name) {return ApplicationContextHolder. instance. appMap. get (name) ;}public Map <String, Object> getAllAttribute () {return ApplicationContextHolder. instance. appMap;} public Set <String> getAllNames () {return ApplicationContextHolder. instance. appMap. keySet ();} public boolean containsName (String name) {return ApplicationContextHolder. instance. appMap. containsKey (name);} public void addSession (String sessionId) {HttpSession httpSession = new HttpSession (); httpSession. setLastVisitTime (new Date (); ApplicationContextHolder. instance. sessionMap. put (sessionId, httpSession);}/*** get session */public HttpSession getSession (HttpExchange httpExchange) {String sessionId = getSessionId (httpExchange); if (StringUtil. isEmpty (sessionId) {return null;} HttpSession httpSession = ApplicationContextHolder. instance. sessionMap. get (sessionId); if (null = httpSession) {httpSession = new HttpSession (); ApplicationContextHolder. instance. sessionMap. put (sessionId, httpSession);} return httpSession;}/*** get sessionId */public String getSessionId (HttpExchange httpExchange) {String cookies = httpExchange. getRequestHeaders (). getFirst ("Cookie"); String sessionId = ""; if (StringUtil. isEmpty (cookies) {cookies = httpExchange. getResponseHeaders (). getFirst ("Set-Cookie");} if (StringUtil. isEmpty (cookies) {return null;} String [] cookiearry = cookies. split (";"); for (String cookie: cookiearry) {cookie = cookie. replaceAll ("", ""); if (cookie. startsWith ("EH_SESSION =") {sessionId = cookie. replace ("EH_SESSION = ",""). replace (";", "") ;}} return sessionId;}/*** get all sessions */public ConcurrentMap <String, HttpSession> getAllSession () {return ApplicationContextHolder. instance. sessionMap;}/*** set the last session access time */public void setSessionLastTime (String sessionId) {HttpSession httpSession = ApplicationContextHolder. instance. sessionMap. get (sessionId); httpSession. setLastVisitTime (new Date ());}}View Code

We can see that the two parts of the Code are very simple. Let's take a look at how to handle the session in handle:

Public void handle (HttpExchange httpExchange) throws IOException {try {String path = httpExchange. getRequestURI (). getPath (); log.info ("Receive a request, Request path:" + path); // set sessionId String sessionId = ApplicationContext. getApplicationContext (). getSessionId (httpExchange); if (StringUtil. isEmpty (sessionId) {sessionId = StringUtil. creatSession (); ApplicationContext. getApplicationContext (). addSession (sessionId );}//..... other Code omitted} catch (Exception e) {httpExchange. close (); log. error ("Response Request failed:", e) ;}/ *** call the corresponding Controller to process the business * @ throws UnsupportedEncodingException */private ResultInfo invokController (HttpExchange httpExchange) throws UnsupportedEncodingException {// obtain the Map <String, Object> map = analysisParms (httpExchange); IndexController controller = new IndexController (); // set session HttpSession httpSession = ApplicationContext. getApplicationContext (). getSession (httpExchange); log.info (httpSession); map. put ("session", httpSession); return controller. process (map );}View Code

Finally, let's take a look at the implementation of scheduled tasks:

/*** Regularly clear expired sessions * @ author guojing * @ date 2014-3-17 */public class SessionCleanTask extends TimerTask {private final Log log = LogFactory. getLog (SessionCleanTask. class); @ Override public void run () {log.info ("Clear session ...... "); ConcurrentMap <String, HttpSession> sessionMap = ApplicationContext. getApplicationContext (). getAllSession (); Iterator <Map. entry <String, HttpSession> it = sessionMap. entrySet (). iterator (); while (it. hasNext () {ConcurrentMap. entry <String, HttpSession> entry = (Entry <String, HttpSession>) it. next (); HttpSession httpSession = entry. getValue (); Date nowDate = new Date (); int diff = (int) (nowDate. getTime ()-httpSession. getLastVisitTime (). getTime ()/1000/60); if (diff> Constants. SESSION_TIMEOUT) {it. remove () ;}} log.info ("clearing session ended ");}}View Code

The code for this change is so much.

Iv. Test

Next, let's test whether it works. Currently, the controller is writable and only one IndexController is available, so we will use this to test it. Let's first change the code of its process method:

Public ResultInfo process (Map <String, Object> map) {ResultInfo result = new ResultInfo (); // here, we determine whether the request contains the name parameter. If yes, it is placed in the session, if no, retrieve the name from the session and put it into the mapHttpSession session = (HttpSession) map. get ("session"); if (map. get ("name ")! = Null) {Object name = map. get ("name"); session. addAttribute ("name", name);} else {Object name = session. getAttribute ("name"); if (name! = Null) {map. put ("name", name) ;}} result. setView ("index"); result. setResultMap (map); return result ;}

We can see that we have added a piece of code. For more information, see annotations. Then we start the server and first access http: // localhost: 8899/page/index. page. The request results are as follows (my logo on the tall will not be truncated ^_^ ):

We can see that the name has no value, so it is not parsed. visit http: // localhost: 8899/page/index. page? Name = guojing. The result is as follows:

This time we found that there was a value, but we know from the code that this should be the value of the request parameter, not obtained from the session. Then we can access http: // localhost: 8899/page/index. page, which should be taken from the session this time. Therefore, guojing can be output as follows:

This indicates that the session has already taken effect. You can check whether the session is valid after the sesion is cleared. The test method for ApplicationContext is the same.

V. Summary

The implementation of this function should be noted to the eye. The implementation of session fundamentally provides support for Dynamic Interaction. Now we can implement login and other functions. However, as mentioned above, the entire project is still very rigid. Currently, we can only use one controller. To implement multiple controllers, We need to judge based on the request parameters, in the next version, we will solve this problem. We will configure multiple controllers through annotations and load them through reflection.
Finally offer benefits, learn-2 source code (corresponding master for the complete project): source code


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.