精华内容
下载资源
问答
  • 快速搭建Android应用后台服务器

    万次阅读 多人点赞 2018-05-08 11:31:42
    一直没单独一个人搭建后台,之前都是用的云服务后台,跟着帖子一步...1.自己动手——快速搭建Android应用服务器 - CSDN博客 https://blog.csdn.net/Mr_Megamind/article/details/71404618 通过这个你可以实现数...

    一直没单独一个人搭建过后台,之前都是用的云服务后台,跟着帖子一步一步走,最终完美实现后台与移动端的数据沟通,顿时感觉棒棒哒,特此记录一下。很感谢下面帖子的博主得无私分享!

    一.后台的搭建

    1.自己动手——快速搭建Android应用服务器 - CSDN博客 https://blog.csdn.net/Mr_Megamind/article/details/71404618

    通过这个你可以实现数据库的建立javaweb应用的搭建后台与移动端通过接口进行数据沟通,网站里只讲了登录接口,但大同小异,自己还需要的接口功能可以依葫芦画瓢慢慢学着编写。

    网站中使用的软件是:MySQL(免费的数据库)+Java Web(遵循Java语言格式的服务器项目,客户端发来的请求的管理与应答者)+Tomcat(服务器软件)+Volley(Google官方的Http请求库,用来写在Android客户端,向服务器端发送请求)来实现我们的服务器。JavaWeb编写使用的是NetBeans软件。
    ps:我实践的时候没有用Volley,用的是xutils框架。

    整个环境搭建需要的软件是:XAMPP、MySQL-Front、NetBeans
    注意:
    1.你安装好XAMPP之后,你其实已经安装了Tomcat了,所以在安装NetBeans时,可以不选择安装其包含的Tomcat了,
    2.在NetBeans里新建项目时,在整个过程中也许会出现配置服务器的对话框:
    这里写图片描述
    这里写图片描述
    这里写图片描述
    服务器选择时,你没有“Apache Tomcat或 TomEE”这个选项的话,就点击添加
    这里写图片描述
    选择“Apache Tomcat或 TomEE”这个选项,再点击下一步,
    这里写图片描述
    点击“浏览”,找到你安装的xampp文件夹里的tomcat文件夹,选中就行啦!!!

    二.JavaWeb应用的编写(NetBeans软件)

    新手请务必成功完成步骤一后再进入此步骤,不然到时报错都不知道去哪里找问题。。。当然如果你很熟练就不需要啦~~~
    1.NetBeans下如何新建运行Java Web - CSDN博客 https://blog.csdn.net/luosisan/article/details/12657439

    2.手把手搭建一个完整的javaweb项目(适合新手) - CSDN博客 https://blog.csdn.net/qq_23994787/article/details/73612870

    展开全文
  • AndroidStudy---搭建后台服务器

    千次阅读 2015-09-07 15:17:20
    我们可能知道在PC端建立一个Server很简单,直接使用Tomcat服务器就可以了,但是我们移动端不可能去搭建一个服务器的,那么我们该怎么办呢?我们可以在网上搜索一下关于如何在手机中搭建一个WebServce,我们可以看到...

    今天终于把老大交代的任务搞完了,感觉收获挺多的,所以就写一篇来记录一下吧,首先还是来看一下,老大们的需求


    需求:

    希望移动端的用户标识(IMEI)和HTML页面的用户标识(Cookie)连接起来,其中HTML页面可能是用户使用PC访问指定的html页面也有可能是移动端使用浏览器访问html页面


    技术解决:

    运用移动设备的系统特型,用Apps的Service监听本地的网络端口,当局域网内或本机的Html页面通过请求调用本地IP:Port时触发监听。Apps在将当前Html的Cookie和移动设备标识物发回服务端时,即实现了本次PC/Mobile Html页面的Cookie和移动设备的互相绑定。


    具体实现方案之多端通信技术方案

    各模块细节
    1.Native App(移动端的app):
    1).跟随主服务常驻内存,在收到当前网络环境为Wifi事件后,启动HttpServer。
    (当前网络切换成3G/2G等网络时关闭HttpServer)。

    以上这步我们叫做注册:启动移动端的HttpServer的时候,我们会将移动设备的ip地址(内网地址)发送到指定的后台Server上
     
    2).收到从Web来的http请求后,往Server发送绑定请求,这时候就将移动端的IMEI和HTML中的Cookie一起上传到指定的Server上
    例如:
    http://serverhost/?cookie=123213&imei=31233213123
    以上这步我们叫做一次绑定的完成操作 


    以为我们是在移动端开始一个HttpServer的,所以要考虑一些额外的因素,比如耗电量
    风险控制:使用灰度测试规则,并使用开关限制功能。
    功耗风险:经过三台设备测试,电量消耗忽略不计(具体CPU使用时间在统计)。
     
    2.Web JS:
    1).加载页面加载后先从CDN加载一个静态的JS到此页面。


    2).静态JS再请求Server 获取一段动态JS用以做本地Ping。
    (如果此IP已有N次获取,则考虑>N的节点是一个大型公共网络,则把此公共IP加入黑名单,不再返回动态JS)


    3).获得动态JS后直接Ping本地的HttpServer。
    (Ping的IP/Port会随着动态JS一起下发) 

    这里我们是在Html页面中使用JS脚本来处理传递Cookie给移动端的功能。


    3.Bridge Server:
    1).Sever选型:Node.js
    同时考虑到开发效率,性能,和client的JS的契合度。
    Node.js单台16核服务器,4节点,QPS经测试能到6k-8k。
    较适合短频非阻塞的请求。
     
    2).数据存储:Redis
    考虑到NativeApp的外网IP时效性,以及对于短频请求的快速应答。
    以外网IP为Key,各个IP为Value。每天清理一次外网IP。
    存储格式:{外网IP:{NativeApp IP/Port, .........}}

    3).移动端所需要的接口
    接口一:Native App 对于本外网IP的注册的请求。
    接口二:Navtive App的绑定请求。

    具体原理图:



    上面讲解了一下技术的实现方案,下面我们就来看一下,具体的实现技术

    首先来看移动端的,我们需要建立一个HttpServer

    我们可能知道在PC端建立一个Server很简单,直接使用Tomcat服务器就可以了,但是我们移动端不可能去搭建一个服务器的,那么我们该怎么办呢?我们可以在网上搜索一下关于如何在手机中搭建一个WebServce,我们可以看到Apache已经帮我们做好了这件事了,他提供了一个jar,有相应的api,同时还有一种技术就是老外有个人写好了一个类:NanoHttpd,我们只需要看懂这个类,然后自己在编写一个类,继承这个类即可,因为这个类是抽象的。这两种搭建的Server原理都是很简单的,就是建立一个Socket连接,这个和Tomcat是一样的,只是他们将这种操作进行优化处理了,那么我们首先来看一下Apache给我们提供的这个HttpServer:

    按照上面的技术方案,我们希望这个HttpServer常驻在一个服务中进行监听的,所以我们需要建立一个Service

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import org.apache.http.client.ClientProtocolException;  
    6.   
    7. import android.app.Service;  
    8. import android.content.BroadcastReceiver;  
    9. import android.content.Context;  
    10. import android.content.Intent;  
    11. import android.content.IntentFilter;  
    12. import android.net.ConnectivityManager;  
    13. import android.os.IBinder;  
    14. import android.util.Log;  
    15.   
    16. public class SocketService extends Service {  
    17.   
    18.     private int port = Consts.defaultPort;  
    19.       
    20.     //网络开关状态改变的监听  
    21.     private BroadcastReceiver receiver = new BroadcastReceiver() {  
    22.         @Override  
    23.         public void onReceive(Context context, Intent intent) {  
    24.             if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)){  
    25.                 AppListYPLog.e("DEMO","网络状态切换...");  
    26.                 //注册接口  
    27.                 new Thread(){  
    28.                     @Override  
    29.                     public void run(){  
    30.                         //开始注册  
    31.                         register();  
    32.                     }  
    33.                 }.start();  
    34.             }  
    35.         }  
    36.     };  
    37.       
    38.     @Override  
    39.     public IBinder onBind(Intent arg0) {  
    40.         return null;  
    41.     }  
    42.       
    43.     @Override  
    44.     public void onCreate() {  
    45.         super.onCreate();  
    46.         Log.v("23233""Complete");  
    47.           
    48.         Utils.init(this);  
    49.           
    50.         //开启监听端口  
    51.         HttpServer hs = new HttpServer();  
    52.         try{  
    53.             AppListYPLog.i("监听开启...");  
    54.             for(int i=0;i<Consts.portAry.length;i++){  
    55.                 if(Utils.checkPort(Consts.portAry[i])){  
    56.                     port = Consts.portAry[i];  
    57.                     break;  
    58.                 }  
    59.             }  
    60.             hs.execute(port);  
    61.             register();  
    62.         }catch(Exception e){  
    63.             AppListYPLog.e("异常:"+e.getMessage());  
    64.         }  
    65.   
    66.         //注册广播  
    67.         IntentFilter filter = new IntentFilter();  
    68.         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);  
    69.         registerReceiver(receiver, filter);  
    70.           
    71.     }  
    72.       
    73.     /** 
    74.      * 注册ip地址 
    75.      * @return 
    76.      */  
    77.     public boolean register(){  
    78.         //拼接参数  
    79.         StringBuilder param = new StringBuilder();  
    80.         param.append("imei=");  
    81.         param.append(Utils.getImeiInfo());  
    82.         param.append("&hs_ip=");  
    83.         param.append(Utils.getLocalHostIp()+":"+port);  
    84.         param.append("&route_mac=");  
    85.         param.append(Utils.getRouteMac());  
    86.         param.append("&route_ssid=");  
    87.         param.append(Utils.getRouteSSID());  
    88.         param.append("&timetag=");  
    89.         param.append(System.currentTimeMillis()+"");  
    90.           
    91.         boolean flag = false;  
    92.           
    93.          //上报数据  
    94.         if(Utils.checkNetWorkState()){  
    95.             try {  
    96.                 flag = NetUtils.uploadRequest(Consts.registerUrl, param.toString());  
    97.                 if(flag){  
    98.                     AppListYPLog.i("注册操作成功");  
    99.                     //Toast.makeText(getApplicationContext(), "注册成功", Toast.LENGTH_LONG).show();  
    100.                 }else{  
    101.                     AppListYPLog.e("注册操作失败");  
    102.                     //Toast.makeText(getApplicationContext(), "注册失败", Toast.LENGTH_LONG).show();  
    103.                 }  
    104.             } catch (ClientProtocolException e) {  
    105.                 AppListYPLog.e("异常:"+e.getMessage()+",注册失败");  
    106.             } catch (IOException e) {  
    107.                 AppListYPLog.e("异常:"+e.getMessage()+",注册失败");  
    108.             } catch(Exception e){  
    109.                 AppListYPLog.e("异常:"+e.getMessage()+",注册失败");  
    110.             }  
    111.         }  
    112.           
    113.         return flag;  
    114.     }  
    115.       
    116. }  
    在这个服务中,我们开始的时候就进行注册,上报一些信息:

    imei:手机移动的标识

    hs_ip:手机的HttpServer的地址和端口号

    route_mac:手机连接的路由器的mac地址

    route_ssid:手机连接的路由器的ssid

    timetag:注册的时间戳


    同时我们还注册了一个监听网络变化的广播,这个是为了防止,开始的时候用户没有开启网络,一旦用户开启网路的时候,我们就开启监听并且进行注册。


    这里我们使用的监听端口是3个,因为我们知道,端口最大的是655535,0-1024是系统使用的,所以我们就使用了三个备用的端口,在使用这个端口之前要检测一下该端口有没有被占用,这里我们就使用Socket探针技术。


    再来看一下最核心的HttpServer的实现:

    首先我们需要下载Apache提供的jar包:

    http://download.csdn.net/detail/jiangwei0910410003/7431829

    代码:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. import java.io.IOException;    
    4. import java.io.InterruptedIOException;    
    5. import java.net.ServerSocket;    
    6. import java.net.Socket;    
    7. import java.util.Locale;    
    8.     
    9.     
    10.   
    11.   
    12.   
    13. import java.util.Map;  
    14.   
    15. import org.apache.http.ConnectionClosedException;    
    16. import org.apache.http.HttpException;    
    17. import org.apache.http.HttpRequest;    
    18. import org.apache.http.HttpResponse;    
    19. import org.apache.http.HttpResponseInterceptor;    
    20. import org.apache.http.HttpServerConnection;    
    21. import org.apache.http.HttpStatus;    
    22. import org.apache.http.MethodNotSupportedException;    
    23. import org.apache.http.entity.StringEntity;    
    24. import org.apache.http.impl.DefaultConnectionReuseStrategy;    
    25. import org.apache.http.impl.DefaultHttpResponseFactory;    
    26. import org.apache.http.impl.DefaultHttpServerConnection;    
    27. import org.apache.http.params.BasicHttpParams;    
    28. import org.apache.http.params.CoreConnectionPNames;    
    29. import org.apache.http.params.CoreProtocolPNames;    
    30. import org.apache.http.params.HttpParams;    
    31. import org.apache.http.protocol.BasicHttpContext;    
    32. import org.apache.http.protocol.HttpContext;    
    33. import org.apache.http.protocol.HttpProcessor;    
    34. import org.apache.http.protocol.HttpRequestHandler;    
    35. import org.apache.http.protocol.HttpRequestHandlerRegistry;    
    36. import org.apache.http.protocol.HttpService;    
    37. import org.apache.http.protocol.ImmutableHttpProcessor;    
    38. import org.apache.http.protocol.ResponseConnControl;    
    39. import org.apache.http.protocol.ResponseContent;    
    40. import org.apache.http.protocol.ResponseDate;    
    41. import org.apache.http.protocol.ResponseServer;    
    42.   
    43. public class HttpServer {   
    44.       
    45.     //获取来访者的ip地址  
    46.     private static String ipAddress = "";  
    47.       
    48.     //开启监听  
    49.     public void execute(int port) throws Exception{  
    50.         Thread t = new RequestListenerThread(port);    
    51.         t.setDaemon(false);    
    52.         t.start();   
    53.     }  
    54.       
    55.     //自定义HttpRequest的处理类,我们需要继承HttpRequestHandler接口  
    56.     static class WebServiceHandler implements HttpRequestHandler {    
    57.     
    58.         public WebServiceHandler() {    
    59.             super();    
    60.         }    
    61.           
    62.         //在这个方法中我们就可以处理请求的业务逻辑  
    63.         public void handle(final HttpRequest request,    
    64.                 final HttpResponse response, final HttpContext context)    
    65.                 throws HttpException, IOException {             
    66.     
    67.             //获取请求方法  
    68.             String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);    
    69.             //获取请求的uri    
    70.             String target = request.getRequestLine().getUri();  
    71.             Map<String,String> params = Utils.getParam(target);  
    72.             //拼接参数  
    73.             StringBuilder param = new StringBuilder();  
    74.             param.append("cookie=");  
    75.             if(params.get("cookie") != null){  
    76.                 param.append(params.get("cookie"));  
    77.             }  
    78.             param.append("&imei=");  
    79.             param.append(Utils.getImeiInfo());  
    80.             param.append("&visitor_ip=");  
    81.             param.append(ipAddress);  
    82.             param.append("&local_ip=");  
    83.             param.append(Utils.getLocalHostIp());  
    84.             param.append("&route_mac=");  
    85.             param.append(Utils.getRouteMac());  
    86.             param.append("&route_ssid=");  
    87.             param.append(Utils.getRouteSSID());  
    88.             param.append("&timetag=");  
    89.             param.append(System.currentTimeMillis()+"");  
    90.               
    91.             //上报数据  
    92.             try{  
    93.                 if(Utils.checkNetWorkState()){  
    94.                     boolean flag = NetUtils.uploadRequest(Consts.cookieUrl, param.toString());  
    95.                     if(flag){  
    96.                         AppListYPLog.e("完成操作成功");  
    97.                     }else{  
    98.                         AppListYPLog.e("完成操作失败");  
    99.                     }  
    100.                 }  
    101.             }catch(Exception e){  
    102.                 AppListYPLog.e("异常:"+e.getMessage()+"完成操作失败");  
    103.             }  
    104.               
    105.             //get请求方式(我们这里只处理get方式)  
    106.             if (method.equals("GET") ) {    
    107.                 response.setStatusCode(HttpStatus.SC_OK);    
    108.                 StringEntity entity = new StringEntity("Request Success!!");    
    109.                 response.setEntity(entity);    
    110.             } else if (method.equals("POST") ) {    
    111.                 response.setStatusCode(HttpStatus.SC_OK);    
    112.                 StringEntity entity = new StringEntity("Request Success!!");    
    113.                 response.setEntity(entity);    
    114.             } else {    
    115.                 throw new MethodNotSupportedException(method + " method not supported");    
    116.             }    
    117.         }    
    118.     
    119.     }    
    120.     
    121.     
    122.     /** 
    123.      * 自定一个监听的线程 
    124.      * @author weijiang204321 
    125.      * 
    126.      */  
    127.     static class RequestListenerThread extends Thread {    
    128.     
    129.         private final ServerSocket serversocket;    
    130.         private final HttpParams params;    
    131.         private final HttpService httpService;    
    132.     
    133.         public RequestListenerThread(int port)    
    134.                 throws IOException {    
    135.               
    136.             /**********************下面就是模板代码了***********************/  
    137.             this.serversocket = new ServerSocket(port);    
    138.     
    139.             HttpProcessor httpproc = new ImmutableHttpProcessor(    
    140.                     new HttpResponseInterceptor[] {    
    141.                             new ResponseDate(), new ResponseServer(),    
    142.                             new ResponseContent(), new ResponseConnControl() });    
    143.             this.params = new BasicHttpParams();    
    144.             this.params    
    145.                     .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)    
    146.                     .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,8 * 1024)    
    147.                     .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)    
    148.                     .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)    
    149.                     .setParameter(CoreProtocolPNames.ORIGIN_SERVER,"HttpComponents/1.1");    
    150.     
    151.     
    152.             //这只请求头信息   
    153.             HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();   
    154.             //WebServiceHandler用来处理webservice请求,这里我们可以注册多个处理器Handler的  
    155.             reqistry.register("*"new WebServiceHandler());      
    156.     
    157.             this.httpService = new HttpService(httpproc,    
    158.                     new DefaultConnectionReuseStrategy(),    
    159.                     new DefaultHttpResponseFactory());    
    160.             httpService.setParams(this.params);  
    161.             //为http服务设置注册好的请求处理器。   
    162.             httpService.setHandlerResolver(reqistry);          
    163.     
    164.         }    
    165.     
    166.         @Override    
    167.         public void run() {    
    168.             AppListYPLog.i("Listening on port " + this.serversocket.getLocalPort());    
    169.             AppListYPLog.i("Thread.interrupted = " + Thread.interrupted());     
    170.             while (!Thread.interrupted()) {    
    171.                 try {    
    172.                     // 建立Http连接    
    173.                     Socket socket = this.serversocket.accept();    
    174.                     DefaultHttpServerConnection conn = new DefaultHttpServerConnection();    
    175.                     ipAddress = socket.getInetAddress().getHostAddress();  
    176.                     AppListYPLog.i("Incoming connection from " + socket.getInetAddress());    
    177.                     conn.bind(socket, this.params);    
    178.                     //开启工作线程    
    179.                     Thread t = new WorkerThread(this.httpService, conn);    
    180.                     t.setDaemon(true);    
    181.                     t.start();    
    182.                 } catch (InterruptedIOException ex) {    
    183.                     AppListYPLog.e("异常:InterruptedIOException");  
    184.                     break;    
    185.                 } catch (IOException e) {    
    186.                     AppListYPLog.e("I/O error initialising connection thread: " + e.getMessage());    
    187.                     break;    
    188.                 }    
    189.             }    
    190.         }    
    191.     }    
    192.     
    193.     
    194.     /** 
    195.      * 后台工作线程 
    196.      * @author weijiang204321 
    197.      * 
    198.      */  
    199.     static class WorkerThread extends Thread {    
    200.     
    201.         private final HttpService httpservice;    
    202.         private final HttpServerConnection conn;    
    203.     
    204.     
    205.         public WorkerThread(final HttpService httpservice,final HttpServerConnection conn) {    
    206.             super();    
    207.             this.httpservice = httpservice;    
    208.             this.conn = conn;    
    209.         }    
    210.     
    211.     
    212.         @Override    
    213.         public void run() {    
    214.             System.out.println("New connection thread");    
    215.             HttpContext context = new BasicHttpContext(null);    
    216.             try {    
    217.                 while (!Thread.interrupted() && this.conn.isOpen()) {    
    218.                     this.httpservice.handleRequest(this.conn, context);    
    219.                 }    
    220.             } catch (ConnectionClosedException ex) {    
    221.                 System.err.println("Client closed connection");    
    222.             } catch (IOException ex) {    
    223.                 System.err.println("I/O error: " + ex.getMessage());    
    224.             } catch (HttpException ex) {    
    225.                 System.err.println("Unrecoverable HTTP protocol violation: "+ ex.getMessage());    
    226.             } finally {    
    227.                 try {    
    228.                     this.conn.shutdown();    
    229.                 } catch (IOException ignore) {    
    230.                 }    
    231.             }    
    232.         }    
    233.     }   
    234.       
    235. }    

    我们首先需要定义一个RequestHander用来处理请求的逻辑结果,同时我们需要建立一个HttpConnection,这里我们需要获取来访者的ip地址,所以我们定义了一个全局的变量ipAddress,然后在WebServiceHandler类中的handle方法中进行上报完成的动作,需要上报的参数为:

    cookie:来访者携带的cookie值(也就是Html页面中的cookie)

    imei:手机的移动标识

    visitor_ip:来访者的ip地址

    local_ip:本机的ip地址

    route_mac:手机连接的路由器的地址

    route_ssid:手机连接的路由器的ssid

    timetag:上报的时间戳


    下面再来看一下辅助类
    Utils:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. import java.io.IOException;  
    4. import java.net.InetAddress;  
    5. import java.net.NetworkInterface;  
    6. import java.net.Socket;  
    7. import java.net.SocketException;  
    8. import java.net.UnknownHostException;  
    9. import java.util.Enumeration;  
    10. import java.util.HashMap;  
    11. import java.util.Map;  
    12.   
    13. import org.apache.http.conn.util.InetAddressUtils;  
    14.   
    15. import android.content.Context;  
    16. import android.net.ConnectivityManager;  
    17. import android.net.NetworkInfo;  
    18. import android.net.wifi.WifiInfo;  
    19. import android.net.wifi.WifiManager;  
    20. import android.telephony.TelephonyManager;  
    21. import android.text.TextUtils;  
    22.   
    23. /** 
    24.  * 工具类 
    25.  *  
    26.  * @author weijiang204321 
    27.  *  
    28.  */  
    29. public class Utils {  
    30.   
    31.     public static Context mContext;  
    32.   
    33.     public static void init(Context context) {  
    34.         mContext = context;  
    35.     }  
    36.   
    37.     /** 
    38.      *  
    39.      * @param str 
    40.      * @return 字符串是否为空 
    41.      */  
    42.     public static boolean isNotEmpty(String str) {  
    43.         if (str != null && !"".equals(str)) {  
    44.             return true;  
    45.         }  
    46.         return false;  
    47.     }  
    48.   
    49.   
    50.     /** 
    51.      * 获取手机的Mac地址 
    52.      *  
    53.      * @return 
    54.      */  
    55.     public static String getMacAddress() {  
    56.         String result = "";  
    57.         try {  
    58.             WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);  
    59.             WifiInfo wifiInfo = wifiManager.getConnectionInfo();  
    60.             result = wifiInfo.getMacAddress();  
    61.         } catch (Exception e) {  
    62.             result = "";  
    63.         }  
    64.         return result;  
    65.     }  
    66.   
    67.     /** 
    68.      * 获取手机的imei 
    69.      *  
    70.      * @return 
    71.      */  
    72.     public static String getImeiInfo() {  
    73.         try {  
    74.             TelephonyManager mTm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);  
    75.             return mTm.getDeviceId();  
    76.         } catch (Exception e) {  
    77.             e.printStackTrace();  
    78.             return "";  
    79.         }  
    80.   
    81.     }  
    82.   
    83.     /** 
    84.      * 获取手机的imsi 
    85.      *  
    86.      * @return 
    87.      */  
    88.     public static String getImsiInfo() {  
    89.         try {  
    90.             String imsi = "";  
    91.             TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);  
    92.             if (telephonyManager != null) {  
    93.                 imsi = telephonyManager.getSubscriberId();  
    94.             }  
    95.             if (TextUtils.isEmpty(imsi)) {  
    96.                 imsi = "UNKNOWN";  
    97.             }  
    98.             return imsi;  
    99.         } catch (Exception e) {  
    100.             return "";  
    101.         }  
    102.     }  
    103.   
    104.     /** 
    105.      * 获取手机型号 
    106.      *  
    107.      * @return 
    108.      */  
    109.     public static String getTypeInfo() {  
    110.         return android.os.Build.MODEL; // 手机型号  
    111.     }  
    112.   
    113.     /** 
    114.      * 获取手机系统版本 
    115.      *  
    116.      * @return 
    117.      */  
    118.     public static String getOsVersion() {  
    119.         return android.os.Build.VERSION.RELEASE;  
    120.     }  
    121.   
    122.     /** 
    123.      * 获取路由器的SSID 
    124.      *  
    125.      * @return 
    126.      */  
    127.     public static String getRouteSSID() {  
    128.         try {  
    129.             WifiManager wm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);  
    130.             WifiInfo info = wm.getConnectionInfo();  
    131.             if(info.getSSID().contains("<")){  
    132.                 return "";  
    133.             }else{  
    134.                 return info.getSSID().replace("\"","") + "";  
    135.             }  
    136.         } catch (Exception e) {  
    137.             AppListYPLog.e("异常:" + e.getMessage() + ",获取SSID失败!");  
    138.             return "";  
    139.         }  
    140.     }  
    141.   
    142.     /** 
    143.      * 获取路由器的Mac地址 
    144.      *  
    145.      * @return 
    146.      */  
    147.     public static String getRouteMac() {  
    148.         try {  
    149.             WifiManager wm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);  
    150.             WifiInfo info = wm.getConnectionInfo();  
    151.             if(info.getBSSID() == null){  
    152.                 return "";  
    153.             }else{  
    154.                 return info.getBSSID() + "";  
    155.             }  
    156.         } catch (Exception e) {  
    157.             AppListYPLog.e("异常:" + e.getMessage() + ",获取Mac地址失败!");  
    158.             return "";  
    159.         }  
    160.     }  
    161.       
    162.     /** 
    163.      * 解析uri参数 
    164.      * @param uri 
    165.      * @return 
    166.      */  
    167.     public static Map<String,String> getParam(String uri){  
    168.         Map<String,String> params = new HashMap<String,String>();  
    169.         try{  
    170.             if(isNotEmpty(uri)){  
    171.                 String subStr = uri.substring(uri.indexOf("?")+1);  
    172.                 String[] ary = subStr.split("&");  
    173.                 for(int i=0;i<ary.length;i++){  
    174.                     String[] temp = ary[i].split("=");  
    175.                     if(temp.length<2){  
    176.                         params.put(temp[0], "");  
    177.                     }else{  
    178.                         params.put(temp[0], temp[1]);  
    179.                     }  
    180.                       
    181.                 }  
    182.                 return params;  
    183.             }else{  
    184.                 return null;  
    185.             }  
    186.         }catch(Exception e){  
    187.             return null;  
    188.         }  
    189.           
    190.     }  
    191.   
    192.     /** 
    193.      * 判断网络 
    194.      * @param ctx 
    195.      * @return 
    196.      */  
    197.     public static boolean checkNetWorkState() {  
    198.         boolean isnetwork = false;  
    199.         ConnectivityManager connManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);  
    200.         NetworkInfo info = connManager.getActiveNetworkInfo();  
    201.   
    202.         if (info != null && (info.isAvailable() || info.isConnected())) {  
    203.             isnetwork = true;  
    204.         }  
    205.         return isnetwork;  
    206.     }  
    207.   
    208.       
    209.     /** 
    210.      * 获取本机的ip 
    211.      * @return 
    212.      */  
    213.     public static String getLocalHostIp(){  
    214.         String ipaddress = "";  
    215.         try{  
    216.             Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();  
    217.             // 遍历所用的网络接口  
    218.             while (en.hasMoreElements()){  
    219.                 NetworkInterface nif = en.nextElement();// 得到每一个网络接口绑定的所有ip  
    220.                 Enumeration<InetAddress> inet = nif.getInetAddresses();  
    221.                 // 遍历每一个接口绑定的所有ip  
    222.                 while (inet.hasMoreElements()){  
    223.                     InetAddress ip = inet.nextElement();  
    224.                     if (!ip.isLoopbackAddress()&& InetAddressUtils.isIPv4Address(ip.getHostAddress())){  
    225.                         return ip.getHostAddress();  
    226.                     }  
    227.                 }  
    228.             }  
    229.         }catch (SocketException e){  
    230.             AppListYPLog.e("feige""获取本地ip地址失败");  
    231.         }  
    232.         return ipaddress;  
    233.     }  
    234.       
    235.     /** 
    236.      * 检查端口port是否被占用了 
    237.      * @param port 
    238.      * @return 
    239.      */  
    240.     public static boolean checkPort(int port){  
    241.         try{  
    242.             InetAddress theAddress=InetAddress.getByName("127.0.0.1");  
    243.             try {  
    244.                 Socket theSocket = new Socket(theAddress,port);  
    245.                 theSocket.close();  
    246.                 theSocket = null;  
    247.                 theAddress = null;  
    248.                 return false;  
    249.             }catch (IOException e) {  
    250.                 AppListYPLog.e("异常:"+e.getMessage()+"检查端口号是否被占用");  
    251.             }catch(Exception e){  
    252.                 AppListYPLog.e("异常:"+e.getMessage()+"检查端口号是否被占用");  
    253.             }  
    254.         }catch(UnknownHostException e) {  
    255.             AppListYPLog.e("异常:"+e.getMessage()+"检查端口号是否被占用");  
    256.         }  
    257.         return true;  
    258.     }  
    259.       
    260. }  

    网络工具类NetUtils

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. import java.io.IOException;  
    4. import java.util.ArrayList;  
    5. import java.util.HashMap;  
    6. import java.util.Iterator;  
    7. import java.util.List;  
    8. import java.util.Map.Entry;  
    9. import java.util.Set;  
    10.   
    11. import org.apache.http.HttpResponse;  
    12. import org.apache.http.NameValuePair;  
    13. import org.apache.http.client.ClientProtocolException;  
    14. import org.apache.http.client.HttpClient;  
    15. import org.apache.http.client.entity.UrlEncodedFormEntity;  
    16. import org.apache.http.client.methods.HttpGet;  
    17. import org.apache.http.client.methods.HttpPost;  
    18. import org.apache.http.impl.client.DefaultHttpClient;  
    19. import org.apache.http.message.BasicNameValuePair;  
    20. import org.apache.http.params.CoreConnectionPNames;  
    21. import org.apache.http.params.HttpConnectionParams;  
    22. import org.apache.http.params.HttpParams;  
    23. import org.apache.http.protocol.HTTP;  
    24. import org.apache.http.util.EntityUtils;  
    25.   
    26. public final class NetUtils {  
    27.       
    28.     /** 
    29.      * 上传数据 
    30.      *  
    31.      * @param url 
    32.      * @param param 
    33.      * @return 
    34.      */  
    35.     public static String postData(String url, HashMap<String, String> param) {  
    36.   
    37.         HttpPost httpPost = new HttpPost(url);  
    38.         // 设置HTTP POST请求参数必须用NameValuePair对象  
    39.         List<NameValuePair> params = new ArrayList<NameValuePair>();  
    40.         if (param != null) {  
    41.             Set<Entry<String, String>> set = param.entrySet();  
    42.             Iterator<Entry<String, String>> iterator = set.iterator();  
    43.             while (iterator.hasNext()) {  
    44.                 Entry<String, String> tempEntry = iterator.next();  
    45.                 params.add(new BasicNameValuePair(tempEntry.getKey(), tempEntry.getValue()));  
    46.             }  
    47.         } else {  
    48.             AppListYPLog.e("DEMO:""上传参数错误");  
    49.             return null;  
    50.         }  
    51.         HttpResponse httpResponse = null;  
    52.         try {  
    53.             // 设置httpPost请求参数  
    54.             httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));  
    55.             HttpClient httpClient = new DefaultHttpClient();  
    56.             // 请求超时  
    57.             httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 20000);  
    58.             // 读取超时  
    59.             httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000);  
    60.             httpResponse = httpClient.execute(httpPost);  
    61.             if (httpResponse.getStatusLine().getStatusCode() == 200) {  
    62.                 // 第三步,使用getEntity方法活得返回结果  
    63.                 String result = EntityUtils.toString(httpResponse.getEntity());  
    64.                 return result;  
    65.             }  
    66.         } catch (ClientProtocolException e) {  
    67.             AppListYPLog.e("异常:" + e.getMessage());  
    68.             return null;  
    69.         } catch (IOException e) {  
    70.             AppListYPLog.e("异常:" + e.getMessage());  
    71.             return null;  
    72.         }  
    73.         return null;  
    74.     }  
    75.       
    76.     /** 
    77.      * @return 通过Request方式曝光 
    78.      */  
    79.     public static boolean uploadRequest(String baseUrl, String param) throws ClientProtocolException, IOException {  
    80.         HttpGet httpGet = null;  
    81.         if (param != null && !"".equals(param)) {  
    82.             httpGet = new HttpGet(baseUrl + "?" + param);  
    83.         } else {  
    84.             httpGet = new HttpGet(baseUrl);  
    85.         }  
    86.         AppListYPLog.e("URL:"+httpGet.getURI()+"");  
    87.         HttpParams params = httpGet.getParams();  
    88.         HttpConnectionParams.setConnectionTimeout(params, 3000);  
    89.         HttpConnectionParams.setSoTimeout(params, 3000);  
    90.         HttpClient httpClient = new DefaultHttpClient(params);  
    91.         HttpResponse response = httpClient.execute(httpGet);  
    92.         int statusCode = response.getStatusLine().getStatusCode();  
    93.         AppListYPLog.e("响应码:" + statusCode);  
    94.         if (statusCode < 300 && statusCode >= 200) {  
    95.             return true;  
    96.         }  
    97.         return false;  
    98.     }  
    99.   
    100.   
    101. }  

    常量类:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. /** 
    4.  * 常量类 
    5.  * @author weijiang204321 
    6.  * 
    7.  */  
    8. public class Consts {  
    9.       
    10.     /*cookie等信息上传url*/  
    11.     public static final String cookieUrl = "http://192.168.1.110:8080/HttpServer/FinishServlets";  
    12.       
    13.     /*注册的url*/  
    14.     public static final String registerUrl = "http://192.168.1.110:8080/HttpServer/RegisterServlets";  
    15.       
    16.     /*备用端口*/  
    17.     public static int[] portAry = new int[]{23021,10034,48990};  
    18.       
    19.     /*默认端口*/  
    20.     public static int defaultPort = 23021;  
    21.       
    22. }  

    打印Log类

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. import android.util.Log;  
    4.   
    5. /** 
    6.  * 打印Log信息 
    7.  * @author weijiang204321 
    8.  * 
    9.  */  
    10. public class AppListYPLog {  
    11.   
    12.     private final static String TAG = "APPLIST";  
    13.     private static boolean SHOW_LOG = true;  
    14.   
    15.     private AppListYPLog() {  
    16.     }  
    17.   
    18.     public static void closeLog() {  
    19.         SHOW_LOG = false;  
    20.     }  
    21.   
    22.     public static void i(String msg) {  
    23.         if (SHOW_LOG) {  
    24.             Log.i(TAG, msg);  
    25.         }  
    26.     }  
    27.   
    28.     public static void d(String msg) {  
    29.         if (SHOW_LOG) {  
    30.             Log.d(TAG, msg);  
    31.         }  
    32.     }  
    33.   
    34.     public static void w(Exception ex) {  
    35.         if (SHOW_LOG) {  
    36.             ex.printStackTrace();  
    37.         }  
    38.     }  
    39.   
    40.     public static void e(String msg) {  
    41.         if (SHOW_LOG) {  
    42.             Log.e(TAG, msg);  
    43.         }  
    44.     }  
    45.   
    46.     public static void i(String tag, String msg) {  
    47.         if (SHOW_LOG) {  
    48.             Log.i(tag, msg);  
    49.         }  
    50.     }  
    51.   
    52.     public static void d(String tag, String msg) {  
    53.         if (SHOW_LOG) {  
    54.             Log.d(tag, msg);  
    55.         }  
    56.     }  
    57.   
    58.     public static void w(String tag, String msg) {  
    59.         if (SHOW_LOG) {  
    60.             Log.w(tag, msg);  
    61.         }  
    62.     }  
    63.   
    64.     public static void e(String tag, String msg) {  
    65.         if (SHOW_LOG) {  
    66.             Log.e(tag, msg);  
    67.         }  
    68.     }  
    69.   
    70. }  


    测试的Demo:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.tests;  
    2.   
    3. import android.app.Activity;  
    4. import android.content.Intent;  
    5. import android.os.Bundle;  
    6.   
    7. public class Main extends Activity {  
    8.   
    9.     @Override  
    10.     protected void onCreate(Bundle savedInstanceState) {  
    11.         super.onCreate(savedInstanceState);  
    12.         setContentView(R.layout.mainlay);  
    13.         startService(new Intent(this, SocketService.class));  
    14.     }  
    15.       
    16. }  


    手机端的HttpServer就搭建完成了,下面我们要想测试的话,就必须在搭建一个Server端,我们这里就简单的建立一个Servlet:

    移动端进行注册的接口:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.weijiang.httpserver;  
    2.   
    3. import java.io.IOException;  
    4. import java.util.Date;  
    5.   
    6. import javax.servlet.ServletException;  
    7. import javax.servlet.http.HttpServlet;  
    8. import javax.servlet.http.HttpServletRequest;  
    9. import javax.servlet.http.HttpServletResponse;  
    10.   
    11. public class RegisterServlets extends HttpServlet {  
    12.   
    13.     private static final long serialVersionUID = 1L;  
    14.   
    15.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {  
    16.         System.out.println("imei:"+req.getParameter("imei"));  
    17.         System.out.println("hs_ip:"+req.getParameter("hs_ip"));  
    18.         System.out.println("route_mac:"+req.getParameter("route_mac"));  
    19.         System.out.println("route_ssid:"+req.getParameter("route_ssid"));  
    20.         System.out.println("timetag:"+new Date(Long.parseLong(req.getParameter("timetag"))));  
    21.     }  
    22.   
    23.     protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {  
    24.         doGet(req,resp);  
    25.     }  
    26.   
    27. }  

    移动端完成操作的接口:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.weijiang.httpserver;  
    2.   
    3. import java.io.IOException;  
    4. import java.util.Date;  
    5.   
    6. import javax.servlet.ServletException;  
    7. import javax.servlet.http.HttpServlet;  
    8. import javax.servlet.http.HttpServletRequest;  
    9. import javax.servlet.http.HttpServletResponse;  
    10.   
    11. public class FinishServlets extends HttpServlet {  
    12.   
    13.     private static final long serialVersionUID = 1L;  
    14.   
    15.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {  
    16.         System.out.println("cookie:"+req.getParameter("cookie"));  
    17.         System.out.println("imei:"+req.getParameter("imei"));  
    18.         System.out.println("visitor_ip:"+req.getParameter("visitor_ip"));  
    19.         System.out.println("local_ip:"+req.getParameter("local_ip"));  
    20.         System.out.println("route_mac:"+req.getParameter("route_mac"));  
    21.         System.out.println("route_ssid:"+req.getParameter("route_ssid"));  
    22.         System.out.println("timetag:"+new Date(Long.parseLong(req.getParameter("timetag"))));  
    23.     }  
    24.   
    25.     protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {  
    26.         doGet(req,resp);  
    27.     }  
    28.   
    29. }  

    模拟携带有cookie值的Html页面

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
    1. <body>  
    2.     <a href='http://192.168.1.109:23021?cookie=123456'>Click</a>  
    3. </body>  
    这里就是为了简单的模拟,所以直接随便写了一个cookie值


    我们将PC和手机进行联网,这里不需要连接在一个网段中,只要在一个公网内就可以了,即他们两个可以ping通就可以,我这里在家里面测试的,用的是自己的路由器,所以一定是在一个网段中,我们查看PC的ip地址:



    手机端的ip地址:



    这样我们下面就可以来进行测试了,首先我们将Android应用部署到手机中,同时我们将Server启动,测试流程很简单:我们尝试改变网络状态,将网路断开,在进行连接,打印Log如下:


    这里我们看到注册成功了,而且将手机的HttpServer的ip地址和监听端口上报了


    我们在Server查看上报的信息:


    我们就可以将手机的的内网地址进行保存,这里我们要注意在前面我们说到,这里还有一个步骤就是要获取公网的ip地址


    上面的注册流程就走通了,下面我们看一下,完成操作的测试:

    我们在浏览器中访问:

    http://localhost:8080/HttpServer/index.jsp

    点击Click,我们看到控制台中Log信息


    我们看到完成操作,将cookie值和imei上报给Server端了,同时记录了来访者的ip地址


    我们在Server的控制台中看到打印信息:


    这样我们就将手机的imei和html页面的cookie值进行绑定了。。


    前面我们说到了搭建移动端的Server的时候还有一种方法就是使用一个老外写的一个简单的类:

    NanoHTTPD:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. package com.sohu.nanohttpd;    
    2.     
    3. import java.io.BufferedReader;    
    4. import java.io.ByteArrayInputStream;    
    5. import java.io.Closeable;    
    6. import java.io.File;    
    7. import java.io.FileInputStream;    
    8. import java.io.FileOutputStream;    
    9. import java.io.IOException;    
    10. import java.io.InputStream;    
    11. import java.io.InputStreamReader;    
    12. import java.io.OutputStream;    
    13. import java.io.PrintWriter;    
    14. import java.io.RandomAccessFile;    
    15. import java.io.PushbackInputStream;    
    16. import java.io.UnsupportedEncodingException;    
    17. import java.net.InetAddress;    
    18. import java.net.InetSocketAddress;    
    19. import java.net.ServerSocket;    
    20. import java.net.Socket;    
    21. import java.net.SocketException;    
    22. import java.net.SocketTimeoutException;    
    23. import java.net.URLDecoder;    
    24. import java.nio.ByteBuffer;    
    25. import java.nio.channels.FileChannel;    
    26. import java.text.SimpleDateFormat;    
    27. import java.util.ArrayList;    
    28. import java.util.Calendar;    
    29. import java.util.Date;    
    30. import java.util.HashMap;    
    31. import java.util.HashSet;    
    32. import java.util.Iterator;    
    33. import java.util.List;    
    34. import java.util.Locale;    
    35. import java.util.Map;    
    36. import java.util.Set;    
    37. import java.util.StringTokenizer;    
    38. import java.util.TimeZone;    
    39.     
    40. /**  
    41.  * A simple, tiny, nicely embeddable HTTP server in Java  
    42.  * <p/>  
    43.  * <p/>  
    44.  * NanoHTTPD  
    45.  * <p></p>Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 2010 by Konstantinos Togias</p>  
    46.  * <p/>  
    47.  * <p/>  
    48.  * <b>Features + limitations: </b>  
    49.  * <ul>  
    50.  * <p/>  
    51.  * <li>Only one Java file</li>  
    52.  * <li>Java 5 compatible</li>  
    53.  * <li>Released as open source, Modified BSD licence</li>  
    54.  * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li>  
    55.  * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li>  
    56.  * <li>Supports both dynamic content and file serving</li>  
    57.  * <li>Supports file upload (since version 1.2, 2010)</li>  
    58.  * <li>Supports partial content (streaming)</li>  
    59.  * <li>Supports ETags</li>  
    60.  * <li>Never caches anything</li>  
    61.  * <li>Doesn't limit bandwidth, request time or simultaneous connections</li>  
    62.  * <li>Default code serves files and shows all HTTP parameters and headers</li>  
    63.  * <li>File server supports directory listing, index.html and index.htm</li>  
    64.  * <li>File server supports partial content (streaming)</li>  
    65.  * <li>File server supports ETags</li>  
    66.  * <li>File server does the 301 redirection trick for directories without '/'</li>  
    67.  * <li>File server supports simple skipping for files (continue download)</li>  
    68.  * <li>File server serves also very long files without memory overhead</li>  
    69.  * <li>Contains a built-in list of most common mime types</li>  
    70.  * <li>All header names are converted lowercase so they don't vary between browsers/clients</li>  
    71.  * <p/>  
    72.  * </ul>  
    73.  * <p/>  
    74.  * <p/>  
    75.  * <b>How to use: </b>  
    76.  * <ul>  
    77.  * <p/>  
    78.  * <li>Subclass and implement serve() and embed to your own program</li>  
    79.  * <p/>  
    80.  * </ul>  
    81.  * <p/>  
    82.  * See the separate "LICENSE.md" file for the distribution license (Modified BSD licence)  
    83.  */    
    84. public abstract class NanoHTTPD {    
    85.     /**  
    86.      * Maximum time to wait on Socket.getInputStream().read() (in milliseconds)  
    87.      * This is required as the Keep-Alive HTTP connections would otherwise  
    88.      * block the socket reading thread forever (or as long the browser is open).  
    89.      */    
    90.     public static final int SOCKET_READ_TIMEOUT = 5000;    
    91.     /**  
    92.      * Common mime type for dynamic content: plain text  
    93.      */    
    94.     public static final String MIME_PLAINTEXT = "text/plain";    
    95.     /**  
    96.      * Common mime type for dynamic content: html  
    97.      */    
    98.     public static final String MIME_HTML = "text/html";    
    99.     /**  
    100.      * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.  
    101.      */    
    102.     private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";    
    103.     private final String hostname;    
    104.     private final int myPort;    
    105.     private ServerSocket myServerSocket;    
    106.     private Set<Socket> openConnections = new HashSet<Socket>();    
    107.     private Thread myThread;    
    108.     /**  
    109.      * Pluggable strategy for asynchronously executing requests.  
    110.      */    
    111.     private AsyncRunner asyncRunner;    
    112.     /**  
    113.      * Pluggable strategy for creating and cleaning up temporary files.  
    114.      */    
    115.     private TempFileManagerFactory tempFileManagerFactory;    
    116.     
    117.     /**  
    118.      * Constructs an HTTP server on given port.  
    119.      */    
    120.     public NanoHTTPD(int port) {    
    121.         this(null, port);    
    122.     }    
    123.     
    124.     /**  
    125.      * Constructs an HTTP server on given hostname and port.  
    126.      */    
    127.     public NanoHTTPD(String hostname, int port) {    
    128.         this.hostname = hostname;    
    129.         this.myPort = port;    
    130.         setTempFileManagerFactory(new DefaultTempFileManagerFactory());    
    131.         setAsyncRunner(new DefaultAsyncRunner());    
    132.     }    
    133.     
    134.     private static final void safeClose(Closeable closeable) {    
    135.         if (closeable != null) {    
    136.             try {    
    137.                 closeable.close();    
    138.             } catch (IOException e) {    
    139.             }    
    140.         }    
    141.     }    
    142.     
    143.     private static final void safeClose(Socket closeable) {    
    144.         if (closeable != null) {    
    145.             try {    
    146.                 closeable.close();    
    147.             } catch (IOException e) {    
    148.             }    
    149.         }    
    150.     }    
    151.     
    152.     private static final void safeClose(ServerSocket closeable) {    
    153.         if (closeable != null) {    
    154.             try {    
    155.                 closeable.close();    
    156.             } catch (IOException e) {    
    157.             }    
    158.         }    
    159.     }    
    160.     
    161.     /**  
    162.      * Start the server.  
    163.      *  
    164.      * @throws IOException if the socket is in use.  
    165.      */    
    166.     public void start() throws IOException {    
    167.         myServerSocket = new ServerSocket();    
    168.         myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));    
    169.     
    170.         myThread = new Thread(new Runnable() {    
    171.             @Override    
    172.             public void run() {    
    173.                 do {    
    174.                     try {    
    175.                         final Socket finalAccept = myServerSocket.accept();    
    176.                         registerConnection(finalAccept);    
    177.                         finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);    
    178.                         final InputStream inputStream = finalAccept.getInputStream();    
    179.                         asyncRunner.exec(new Runnable() {    
    180.                             @Override    
    181.                             public void run() {    
    182.                                 OutputStream outputStream = null;    
    183.                                 try {    
    184.                                     outputStream = finalAccept.getOutputStream();    
    185.                                     TempFileManager tempFileManager = tempFileManagerFactory.create();    
    186.                                     HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());    
    187.                                     while (!finalAccept.isClosed()) {    
    188.                                         session.execute();    
    189.                                     }    
    190.                                 } catch (Exception e) {    
    191.                                     // When the socket is closed by the client, we throw our own SocketException    
    192.                                     // to break the  "keep alive" loop above.    
    193.                                     if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {    
    194.                                         e.printStackTrace();    
    195.                                     }    
    196.                                 } finally {    
    197.                                     safeClose(outputStream);    
    198.                                     safeClose(inputStream);    
    199.                                     safeClose(finalAccept);    
    200.                                     unRegisterConnection(finalAccept);    
    201.                                 }    
    202.                             }    
    203.                         });    
    204.                     } catch (IOException e) {    
    205.                     }    
    206.                 } while (!myServerSocket.isClosed());    
    207.             }    
    208.         });    
    209.         myThread.setDaemon(true);    
    210.         myThread.setName("NanoHttpd Main Listener");    
    211.         myThread.start();    
    212.     }    
    213.     
    214.     /**  
    215.      * Stop the server.  
    216.      */    
    217.     public void stop() {    
    218.         try {    
    219.             safeClose(myServerSocket);    
    220.             closeAllConnections();    
    221.             myThread.join();    
    222.         } catch (Exception e) {    
    223.             e.printStackTrace();    
    224.         }    
    225.     }    
    226.     
    227.     /**  
    228.      * Registers that a new connection has been set up.  
    229.      *  
    230.      * @param socket  
    231.      *            the {@link Socket} for the connection.  
    232.      */    
    233.     public synchronized void registerConnection(Socket socket) {    
    234.         openConnections.add(socket);    
    235.     }    
    236.     
    237.     /**  
    238.      * Registers that a connection has been closed  
    239.      *  
    240.      * @param socket  
    241.      *            the {@link Socket} for the connection.  
    242.      */    
    243.     public synchronized void unRegisterConnection(Socket socket) {    
    244.         openConnections.remove(socket);    
    245.     }    
    246.     
    247.     /**  
    248.      * Forcibly closes all connections that are open.  
    249.      */    
    250.     public synchronized void closeAllConnections() {    
    251.         for (Socket socket : openConnections) {    
    252.             safeClose(socket);    
    253.         }    
    254.     }    
    255.     
    256.     public final int getListeningPort() {    
    257.         return myServerSocket == null ? -1 : myServerSocket.getLocalPort();    
    258.     }    
    259.     
    260.     public final boolean wasStarted() {    
    261.         return myServerSocket != null && myThread != null;    
    262.     }    
    263.     
    264.     public final boolean isAlive() {    
    265.         return wasStarted() && !myServerSocket.isClosed() && myThread.isAlive();    
    266.     }    
    267.     
    268.     /**  
    269.      * Override this to customize the server.  
    270.      * <p/>  
    271.      * <p/>  
    272.      * (By default, this delegates to serveFile() and allows directory listing.)  
    273.      *  
    274.      * @param uri     Percent-decoded URI without parameters, for example "/index.cgi"  
    275.      * @param method  "GET", "POST" etc.  
    276.      * @param parms   Parsed, percent decoded parameters from URI and, in case of POST, data.  
    277.      * @param headers Header entries, percent decoded  
    278.      * @return HTTP response, see class Response for details  
    279.      */    
    280.     @Deprecated    
    281.     public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,    
    282.                                    Map<String, String> files) {    
    283.         return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found");    
    284.     }    
    285.     
    286.     /**  
    287.      * Override this to customize the server.  
    288.      * <p/>  
    289.      * <p/>  
    290.      * (By default, this delegates to serveFile() and allows directory listing.)  
    291.      *  
    292.      * @param session The HTTP session  
    293.      * @return HTTP response, see class Response for details  
    294.      * 我们需要自己实现这个方法  
    295.      */    
    296.     public Response serve(IHTTPSession session) {    
    297.         Map<String, String> files = new HashMap<String, String>();    
    298.         Method method = session.getMethod();    
    299.         if (Method.PUT.equals(method) || Method.POST.equals(method)) {    
    300.             try {    
    301.                 session.parseBody(files);    
    302.             } catch (IOException ioe) {    
    303.                 return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());    
    304.             } catch (ResponseException re) {    
    305.                 return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());    
    306.             }    
    307.         }    
    308.     
    309.         Map<String, String> parms = session.getParms();    
    310.         parms.put(QUERY_STRING_PARAMETER, session.getQueryParameterString());    
    311.         return serve(session.getUri(), method, session.getHeaders(), parms, files);    
    312.     }    
    313.     
    314.     /**  
    315.      * Decode percent encoded <code>String</code> values.  
    316.      *  
    317.      * @param str the percent encoded <code>String</code>  
    318.      * @return expanded form of the input, for example "foo%20bar" becomes "foo bar"  
    319.      */    
    320.     protected String decodePercent(String str) {    
    321.         String decoded = null;    
    322.         try {    
    323.             decoded = URLDecoder.decode(str, "UTF8");    
    324.         } catch (UnsupportedEncodingException ignored) {    
    325.         }    
    326.         return decoded;    
    327.     }    
    328.     
    329.     /**  
    330.      * Decode parameters from a URL, handing the case where a single parameter name might have been  
    331.      * supplied several times, by return lists of values.  In general these lists will contain a single  
    332.      * element.  
    333.      *  
    334.      * @param parms original <b>NanoHttpd</b> parameters values, as passed to the <code>serve()</code> method.  
    335.      * @return a map of <code>String</code> (parameter name) to <code>List<String></code> (a list of the values supplied).  
    336.      */    
    337.     protected Map<String, List<String>> decodeParameters(Map<String, String> parms) {    
    338.         return this.decodeParameters(parms.get(QUERY_STRING_PARAMETER));    
    339.     }    
    340.     
    341.     /**  
    342.      * Decode parameters from a URL, handing the case where a single parameter name might have been  
    343.      * supplied several times, by return lists of values.  In general these lists will contain a single  
    344.      * element.  
    345.      *  
    346.      * @param queryString a query string pulled from the URL.  
    347.      * @return a map of <code>String</code> (parameter name) to <code>List<String></code> (a list of the values supplied).  
    348.      */    
    349.     protected Map<String, List<String>> decodeParameters(String queryString) {    
    350.         Map<String, List<String>> parms = new HashMap<String, List<String>>();    
    351.         if (queryString != null) {    
    352.             StringTokenizer st = new StringTokenizer(queryString, "&");    
    353.             while (st.hasMoreTokens()) {    
    354.                 String e = st.nextToken();    
    355.                 int sep = e.indexOf('=');    
    356.                 String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();    
    357.                 if (!parms.containsKey(propertyName)) {    
    358.                     parms.put(propertyName, new ArrayList<String>());    
    359.                 }    
    360.                 String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null;    
    361.                 if (propertyValue != null) {    
    362.                     parms.get(propertyName).add(propertyValue);    
    363.                 }    
    364.             }    
    365.         }    
    366.         return parms;    
    367.     }    
    368.     
    369.     // ------------------------------------------------------------------------------- //    
    370.     //    
    371.     // Threading Strategy.    
    372.     //    
    373.     // ------------------------------------------------------------------------------- //    
    374.     
    375.     /**  
    376.      * Pluggable strategy for asynchronously executing requests.  
    377.      *  
    378.      * @param asyncRunner new strategy for handling threads.  
    379.      */    
    380.     public void setAsyncRunner(AsyncRunner asyncRunner) {    
    381.         this.asyncRunner = asyncRunner;    
    382.     }    
    383.     
    384.     // ------------------------------------------------------------------------------- //    
    385.     //    
    386.     // Temp file handling strategy.    
    387.     //    
    388.     // ------------------------------------------------------------------------------- //    
    389.     
    390.     /**  
    391.      * Pluggable strategy for creating and cleaning up temporary files.  
    392.      *  
    393.      * @param tempFileManagerFactory new strategy for handling temp files.  
    394.      */    
    395.     public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {    
    396.         this.tempFileManagerFactory = tempFileManagerFactory;    
    397.     }    
    398.     
    399.     /**  
    400.      * HTTP Request methods, with the ability to decode a <code>String</code> back to its enum value.  
    401.      */    
    402.     public enum Method {    
    403.         GET, PUT, POST, DELETE, HEAD, OPTIONS;    
    404.     
    405.         static Method lookup(String method) {    
    406.             for (Method m : Method.values()) {    
    407.                 if (m.toString().equalsIgnoreCase(method)) {    
    408.                     return m;    
    409.                 }    
    410.             }    
    411.             return null;    
    412.         }    
    413.     }    
    414.     
    415.     /**  
    416.      * Pluggable strategy for asynchronously executing requests.  
    417.      */    
    418.     public interface AsyncRunner {    
    419.         void exec(Runnable code);    
    420.     }    
    421.     
    422.     /**  
    423.      * Factory to create temp file managers.  
    424.      */    
    425.     public interface TempFileManagerFactory {    
    426.         TempFileManager create();    
    427.     }    
    428.     
    429.     // ------------------------------------------------------------------------------- //    
    430.     
    431.     /**  
    432.      * Temp file manager.  
    433.      * <p/>  
    434.      * <p>Temp file managers are created 1-to-1 with incoming requests, to create and cleanup  
    435.      * temporary files created as a result of handling the request.</p>  
    436.      */    
    437.     public interface TempFileManager {    
    438.         TempFile createTempFile() throws Exception;    
    439.     
    440.         void clear();    
    441.     }    
    442.     
    443.     /**  
    444.      * A temp file.  
    445.      * <p/>  
    446.      * <p>Temp files are responsible for managing the actual temporary storage and cleaning  
    447.      * themselves up when no longer needed.</p>  
    448.      */    
    449.     public interface TempFile {    
    450.         OutputStream open() throws Exception;    
    451.     
    452.         void delete() throws Exception;    
    453.     
    454.         String getName();    
    455.     }    
    456.     
    457.     /**  
    458.      * Default threading strategy for NanoHttpd.  
    459.      * <p/>  
    460.      * <p>By default, the server spawns a new Thread for every incoming request.  These are set  
    461.      * to <i>daemon</i> status, and named according to the request number.  The name is  
    462.      * useful when profiling the application.</p>  
    463.      */    
    464.     public static class DefaultAsyncRunner implements AsyncRunner {    
    465.         private long requestCount;    
    466.     
    467.         @Override    
    468.         public void exec(Runnable code) {    
    469.             ++requestCount;    
    470.             Thread t = new Thread(code);    
    471.             t.setDaemon(true);    
    472.             t.setName("NanoHttpd Request Processor (#" + requestCount + ")");    
    473.             t.start();    
    474.         }    
    475.     }    
    476.     
    477.     /**  
    478.      * Default strategy for creating and cleaning up temporary files.  
    479.      * <p/>  
    480.      * <p></p>This class stores its files in the standard location (that is,  
    481.      * wherever <code>java.io.tmpdir</code> points to).  Files are added  
    482.      * to an internal list, and deleted when no longer needed (that is,  
    483.      * when <code>clear()</code> is invoked at the end of processing a  
    484.      * request).</p>  
    485.      */    
    486.     public static class DefaultTempFileManager implements TempFileManager {    
    487.         private final String tmpdir;    
    488.         private final List<TempFile> tempFiles;    
    489.     
    490.         public DefaultTempFileManager() {    
    491.             tmpdir = System.getProperty("java.io.tmpdir");    
    492.             tempFiles = new ArrayList<TempFile>();    
    493.         }    
    494.     
    495.         @Override    
    496.         public TempFile createTempFile() throws Exception {    
    497.             DefaultTempFile tempFile = new DefaultTempFile(tmpdir);    
    498.             tempFiles.add(tempFile);    
    499.             return tempFile;    
    500.         }    
    501.     
    502.         @Override    
    503.         public void clear() {    
    504.             for (TempFile file : tempFiles) {    
    505.                 try {    
    506.                     file.delete();    
    507.                 } catch (Exception ignored) {    
    508.                 }    
    509.             }    
    510.             tempFiles.clear();    
    511.         }    
    512.     }    
    513.     
    514.     /**  
    515.      * Default strategy for creating and cleaning up temporary files.  
    516.      * <p/>  
    517.      * <p></p></[>By default, files are created by <code>File.createTempFile()</code> in  
    518.      * the directory specified.</p>  
    519.      */    
    520.     public static class DefaultTempFile implements TempFile {    
    521.         private File file;    
    522.         private OutputStream fstream;    
    523.     
    524.         public DefaultTempFile(String tempdir) throws IOException {    
    525.             file = File.createTempFile("NanoHTTPD-"""new File(tempdir));    
    526.             fstream = new FileOutputStream(file);    
    527.         }    
    528.     
    529.         @Override    
    530.         public OutputStream open() throws Exception {    
    531.             return fstream;    
    532.         }    
    533.     
    534.         @Override    
    535.         public void delete() throws Exception {    
    536.             safeClose(fstream);    
    537.             file.delete();    
    538.         }    
    539.     
    540.         @Override    
    541.         public String getName() {    
    542.             return file.getAbsolutePath();    
    543.         }    
    544.     }    
    545.     
    546.     /**  
    547.      * HTTP response. Return one of these from serve().  
    548.      */    
    549.     public static class Response {    
    550.         /**  
    551.          * HTTP status code after processing, e.g. "200 OK", HTTP_OK  
    552.          */    
    553.         private Status status;    
    554.         /**  
    555.          * MIME type of content, e.g. "text/html"  
    556.          */    
    557.         private String mimeType;    
    558.         /**  
    559.          * Data of the response, may be null.  
    560.          */    
    561.         private InputStream data;    
    562.         /**  
    563.          * Headers for the HTTP response. Use addHeader() to add lines.  
    564.          */    
    565.         private Map<String, String> header = new HashMap<String, String>();    
    566.         /**  
    567.          * The request method that spawned this response.  
    568.          */    
    569.         private Method requestMethod;    
    570.         /**  
    571.          * Use chunkedTransfer  
    572.          */    
    573.         private boolean chunkedTransfer;    
    574.     
    575.         /**  
    576.          * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message  
    577.          */    
    578.         public Response(String msg) {    
    579.             this(Status.OK, MIME_HTML, msg);    
    580.         }    
    581.     
    582.         /**  
    583.          * Basic constructor.  
    584.          */    
    585.         public Response(Status status, String mimeType, InputStream data) {    
    586.             this.status = status;    
    587.             this.mimeType = mimeType;    
    588.             this.data = data;    
    589.         }    
    590.     
    591.         /**  
    592.          * Convenience method that makes an InputStream out of given text.  
    593.          */    
    594.         public Response(Status status, String mimeType, String txt) {    
    595.             this.status = status;    
    596.             this.mimeType = mimeType;    
    597.             try {    
    598.                 this.data = txt != null ? new ByteArrayInputStream(txt.getBytes("UTF-8")) : null;    
    599.             } catch (java.io.UnsupportedEncodingException uee) {    
    600.                 uee.printStackTrace();    
    601.             }    
    602.         }    
    603.     
    604.         /**  
    605.          * Adds given line to the header.  
    606.          */    
    607.         public void addHeader(String name, String value) {    
    608.             header.put(name, value);    
    609.         }    
    610.     
    611.         /**  
    612.          * Sends given response to the socket.  
    613.          */    
    614.         private void send(OutputStream outputStream) {    
    615.             String mime = mimeType;    
    616.             SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);    
    617.             gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));    
    618.     
    619.             try {    
    620.                 if (status == null) {    
    621.                     throw new Error("sendResponse(): Status can't be null.");    
    622.                 }    
    623.                 PrintWriter pw = new PrintWriter(outputStream);    
    624.                 pw.print("HTTP/1.1 " + status.getDescription() + " \r\n");    
    625.     
    626.                 if (mime != null) {    
    627.                     pw.print("Content-Type: " + mime + "\r\n");    
    628.                 }    
    629.     
    630.                 if (header == null || header.get("Date") == null) {    
    631.                     pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n");    
    632.                 }    
    633.     
    634.                 if (header != null) {    
    635.