Android中如何搭建一个WebServer

本文介绍如何在Android应用中搭建一个WebServer,以实现移动端IMEI与HTML页面Cookie的绑定。通过监听本地网络端口,当HTML页面请求时,将Cookie和IMEI发送至服务器。详细讲解了移动端HttpServer的实现、Web JS处理以及Bridge Server的设计。
摘要由CSDN通过智能技术生成

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

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


需求:

希望移动端的用户标识(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

package com.sohu.tests;import java.io.IOException;import org.apache.http.client.ClientProtocolException;import android.app.Service;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.os.IBinder;import android.util.Log;public class SocketService extends Service private int port = Consts.defaultPort;  //网络开关状态改变的监听 private BroadcastReceiver receiver = new BroadcastReceiver() {  @Override  public void onReceive(Context context, Intent intent) {   if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)){    AppListYPLog.e("DEMO","网络状态切换...");    //注册接口    new Thread(){     @Override     public void run(){      //开始注册      register();     }    }.start();   }  } };  @Override public IBinder onBind(Intent arg0) {  return null; }  @Override public void onCreate() {  super.onCreate();  Log.v("23233", "Complete");    Utils.init(this);    //开启监听端口  HttpServer hs = new HttpServer();  try{   AppListYPLog.i("监听开启...");   for(int i=0;i<Consts.portAry.length;i++){    if(Utils.checkPort(Consts.portAry[i])){     port = Consts.portAry[i];     break;    }   }   hs.execute(port);   register();  }catch(Exception e){   AppListYPLog.e("异常:"+e.getMessage());  }  //注册广播  IntentFilter filter = new IntentFilter();  filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);  registerReceiver(receiver, filter);   }  /**  * 注册ip地址  * @return  */ public boolean register(){  //拼接参数        StringBuilder param = new StringBuilder();        param.append("imei=");        param.append(Utils.getImeiInfo());        param.append("&hs_ip=");        param.append(Utils.getLocalHostIp()+":"+port);        param.append("&route_mac=");        param.append(Utils.getRouteMac());        param.append("&route_ssid=");        param.append(Utils.getRouteSSID());        param.append("&timetag=");        param.append(System.currentTimeMillis()+"");                boolean flag = false;           //上报数据        if(Utils.checkNetWorkState()){         try {    flag = NetUtils.uploadRequest(Consts.registerUrl, param.toString());    if(flag){     AppListYPLog.i("注册操作成功");     //Toast.makeText(getApplicationContext(), "注册成功", Toast.LENGTH_LONG).show();    }else{     AppListYPLog.e("注册操作失败");     //Toast.makeText(getApplicationContext(), "注册失败", Toast.LENGTH_LONG).show();    }   } catch (ClientProtocolException e) {    AppListYPLog.e("异常:"+e.getMessage()+",注册失败");   } catch (IOException e) {    AppListYPLog.e("异常:"+e.getMessage()+",注册失败");   } catch(Exception e){    AppListYPLog.e("异常:"+e.getMessage()+",注册失败");   }        }                return flag; } }
在这个服务中,我们开始的时候就进行注册,上报一些信息:

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

代码:

package com.sohu.tests;import java.io.IOException;  import java.io.InterruptedIOException;  import java.net.ServerSocket;  import java.net.Socket;  import java.util.Locale;      import java.util.Map;import org.apache.http.ConnectionClosedException;  import org.apache.http.HttpException;  import org.apache.http.HttpRequest;  import org.apache.http.HttpResponse;  import org.apache.http.HttpResponseInterceptor;  import org.apache.http.HttpServerConnection;  import org.apache.http.HttpStatus;  import org.apache.http.MethodNotSupportedException;  import org.apache.http.entity.StringEntity;  import org.apache.http.impl.DefaultConnectionReuseStrategy;  import org.apache.http.impl.DefaultHttpResponseFactory;  import org.apache.http.impl.DefaultHttpServerConnection;  import org.apache.http.params.BasicHttpParams;  import org.apache.http.params.CoreConnectionPNames;  import org.apache.http.params.CoreProtocolPNames;  import org.apache.http.params.HttpParams;  import org.apache.http.protocol.BasicHttpContext;  import org.apache.http.protocol.HttpContext;  import org.apache.http.protocol.HttpProcessor;  import org.apache.http.protocol.HttpRequestHandler;  import org.apache.http.protocol.HttpRequestHandlerRegistry;  import org.apache.http.protocol.HttpService;  import org.apache.http.protocol.ImmutableHttpProcessor;  import org.apache.http.protocol.ResponseConnControl;  import org.apache.http.protocol.ResponseContent;  import org.apache.http.protocol.ResponseDate;  import org.apache.http.protocol.ResponseServer;  public class HttpServer {   //获取来访者的ip地址 private static String ipAddress = "";  //开启监听    public void execute(int port) throws Exception{     Thread t = new RequestListenerThread(port);          t.setDaemon(false);          t.start();     }        //自定义HttpRequest的处理类,我们需要继承HttpRequestHandler接口    static class WebServiceHandler implements HttpRequestHandler {            public WebServiceHandler() {              super();          }                  //在这个方法中我们就可以处理请求的业务逻辑        public void handle(final HttpRequest request,                  final HttpResponse response, final HttpContext context)                  throws HttpException, IOException {                      //获取请求方法            String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);              //获取请求的uri              String target = request.getRequestLine().getUri();            Map<String,String> params = Utils.getParam(target);            //拼接参数            StringBuilder param = new StringBuilder();            param.append("cookie=");            if(params.get("cookie") != null){             param.append(params.get("cookie"));            }            param.append("&imei=");            param.append(Utils.getImeiInfo());            param.append("&visitor_ip=");            param.append(ipAddress);            param.append("&local_ip=");            param.append(Utils.getLocalHostIp());            param.append("&route_mac=");            param.append(Utils.getRouteMac());            param.append("&route_ssid=");            param.append(Utils.getRouteSSID());            param.append("&timetag=");            param.append(System.currentTimeMillis()+"");                        //上报数据            try{             if(Utils.checkNetWorkState()){                 boolean flag = NetUtils.uploadRequest(Consts.cookieUrl, param.toString());                 if(flag){         AppListYPLog.e("完成操作成功");        }else{         AppListYPLog.e("完成操作失败");        }                }            }catch(Exception e){             AppListYPLog.e("异常:"+e.getMessage()+"完成操作失败");            }                        //get请求方式(我们这里只处理get方式)            if (method.equals("GET") ) {                  response.setStatusCode(HttpStatus.SC_OK);                  StringEntity entity = new StringEntity("Request Success!!");                  response.setEntity(entity);              } else if (method.equals("POST") ) {                  response.setStatusCode(HttpStatus.SC_OK);                  StringEntity entity = new StringEntity("Request Success!!");                  response.setEntity(entity);              } else {                  throw new MethodNotSupportedException(method + " method not supported");              }          }        }          /**     * 自定一个监听的线程     * @author weijiang204321     *     */    static class RequestListenerThread extends Thread {            private final ServerSocket serversocket;          private final HttpParams params;          private final HttpService httpService;            public
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值