如果你的应用需要与服务器端的Web程序进行交互,肯定与遇到我下面说的这个问题。
有天开发Web应用的小孙告诉我,他的Web应用从tomcat控制台查看,连接数上百了,这对于正常的应用没什么,但是我们是测试环境啊,只有我们三个Android开发人员,哪有那么多的链接数呢,最后发现,只要我点击手机上的关于服务器交互的应用,就会产生一个连接数,这可要人命,以前没开发过android的正式产品。最后发现原来是如果浏览器访问Web端,一个用户产生一个Session连接,由于手机上开发时session需要自己的程序管理,我们又不知道还要这么搞才出现这种情况。搜索各大帖子,大家的普遍做法是:获取服务器端的一个sessionId,然后保存到一个变量里,每次请求的时候将SessionId传递过去,这样服务器端接到请求以后,发现系统中这个session是存在的,就不会生成新的了。
代码如下:- public static String httpPost(Map<String, Object> map) {
- String result = ""; // 获取服务器返回数据
- String param = JSON.toJSONString(map);
-
- // Log.i(App.Log.app_name,"加密前的数据:"+param);
-
- String method = map.get("method").toString();
-
- // 加密
- BASE64Encoder base64encoder = new BASE64Encoder();
- String sendMsg = null;
- HttpURLConnection urlConn=null;
- try {
- sendMsg = base64encoder.encode(param.getBytes("utf-8"));
- // Log.i(App.Log.app_name,"加密后的数据:"+sendMsg);
-
- if (sendMsg != null) {
- URL _url = new URL(SERVICEURL);
- //_url.openConnection(proxy);
- urlConn= (HttpURLConnection) _url.openConnection();
- createSession();
- if(null!=App.sessionId || !"".equals(App.sessionId)){
- Log.e(App.Log.app_name,"App.sessionId="+App.sessionId);
- urlConn.setRequestProperty("Cookie", App.sessionId);
- }
-
- urlConn.setDoInput(true); // 设置输入流采用字节流
- urlConn.setDoOutput(true); // 设置输出流采用字节流
- urlConn.setRequestMethod("POST");
- urlConn.setUseCaches(false); // 设置缓存
- urlConn.setRequestProperty("Charset", "utf-8");
- urlConn.setConnectTimeout(CONNECTION_TIMEOUT_INT);
- urlConn.setReadTimeout(READ_TIMEOUT_INT);
- urlConn.setRequestProperty("Content-Type", "application/json");
- urlConn.setRequestProperty("Accept", "application/json");
-
- //urlConn.getAllowUserInteraction();
- urlConn.getPermission();
- urlConn.connect(); // 连接既往服务端发送消息
-
- BufferedOutputStream dop = new BufferedOutputStream(
- urlConn.getOutputStream());
-
- dop.write(sendMsg.getBytes("utf-8")); // 发送参数
- dop.flush(); // 发送,清空缓存
- dop.close(); // 关闭
- int status = urlConn.getResponseCode();
- Log.e(App.Log.app_name,urlConn.getResponseMessage());
- if (status != 200) {
-
- } else {
- // 下面开始做接收工作
- BufferedReader bufferReader = new BufferedReader(
- new InputStreamReader(urlConn.getInputStream()));
-
- String readLine = null;
- while ((readLine = bufferReader.readLine()) != null) {
- result += readLine;
- }
- bufferReader.close();
- urlConn.disconnect();
- }
- return result;
- } else {
- System.out.println("param is null");
- }
-
- } catch (Exception e) {
-
- if ("sendCallinLog".equals(method)) {
- Log.i(App.Log.app_name, "来电信息传递发生异常");
- result = "false";
- e.printStackTrace();
- } else {
- e.printStackTrace();
- }
-
- }
- return result;
- }
复制代码 特别是这几句话是我刚修改的:- if(null!=App.sessionId || !"".equals(App.sessionId)){
- Log.e(App.Log.app_name,"App.sessionId="+App.sessionId);
- urlConn.setRequestProperty("Cookie", App.sessionId);
- }
复制代码 添加一个Cookie的属性,传递过去。
接着就是如何获取sessionId,以及何时生成了。
其实获取很容易:
我单独写了一个方法:- public static String getSessionId(){
-
- URL _url;
- String sessionid=null;
- try {
- _url = new URL(SERVICEURL);
-
- HttpURLConnection con= (HttpURLConnection) _url.openConnection();
-
- // 取得sessionid.
- String cookieval = con.getHeaderField("Set-Cookie");
-
- if(cookieval != null) {
- sessionid = cookieval.substring(0, cookieval.indexOf(";"));
- }
-
-
- //App.sessionId=sessionid;
-
- con.disconnect();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- return sessionid;
- }
-
复制代码 说到这个方法,大家会想,多次一举,为什么创建一个URL,又openConnection()。在httpPost方法里获取不就可以了吗?我尝试了很多次了,会报错的。查阅了一下,大概的意思是
String cookieval = con.getHeaderField("Set-Cookie");
这句代码不能放在 urlConn.setRequestProperty("Cookie", App.sessionId); 前面执行,也就是没办法同时用一个connection对象即获得sessionId又将sessionID传递到服务器端。只好又生成一个,而且每次调用openConnection(),服务器端都会生成一个新的session。
以上是一个插曲。小弟才疏学浅,不明白其中缘由,哪位大哥知道的话可以留言啊。
接着说,有生成sessionId的方法,有传递的载体了,我们需要考虑何时生成sessionId,因为每调用一次生成SessionId的方法就会生成一个连接的。
时机是:当我们的全局变量为空的时候生成。但是问题来了,如果用户长时间不操作,呆在一个界面,一会服务器端的session失效了,他又开始操作界面的时候,服务器端又会不断生成链接,为什么呢?因为我们每次传递给他的sessionId都是过期的啊。
完美的解决方法是:我们能通过一种途径去获取到服务器对应的session已失效,但是,我没有找到。
最后用了一个折中的办法,让客户端生成一个时间戳,开始计时,如果超过15分钟不操作界面,那么我们就获取一个新的sessionId(服务器端定的session有效期为20分钟,也就是在它没有失效之前就已经生成新的sessionId了,这样session就永远不会失效了)。代码如下:- public synchronized static void createSession() {
-
-
- if(null==App.sessionId || "".equals(App.sessionId)){
- App.sessionId=getSessionId();
- System.out.println("session创建!");
- sessionCreateTime=new Date().getTime();
- }
-
- //20分钟后更换session
- long nowTime=new Date().getTime();
- int min=(int)((nowTime-sessionCreateTime)/(1000*60));
- if(min>=15){
- App.sessionId=getSessionId();
- System.out.println("session重新创建!");
- sessionCreateTime=new Date().getTime();
- }else{
- sessionCreateTime=new Date().getTime();
- }
-
- }
复制代码 这样,已登录成功就会只有一个或者两个session了。
貌似解决了问题,但是,如果客户端没有退出,这时服务器端重启了,那么情况就又回到了原来的情况,不过正是的应用,服务器端重启都是在用户都不太使用的时间段。但是如果能获取到服务器端的session已失效这种状态就彻底可以解决这个问题了。
以上就是我的一个思路与解决方案,我把它写了下来,你呢?说说你的看法吧。
原文链接:http://www./thread-292239-1-44.html
|