分享一下我老师大神的人工智能教程!零基础,通俗易懂!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探针技术。
首先我们需要下载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