• Anroid搭建一个局域网Web服务器


    前言

     

      很多开发者看到这个标题表示很怪异,Android怎么可能搭建服务器呢?根本用不到呀,这个项目毫无价值。我表示很理解这一类的开发者,毕竟每个人的经验经历都是有限的。

     

      必须要说说我们的用处(需要用这个功能的人自然不用解释),比如在TV开发中,现在我们有一个电视盒子,上面跑着我们的一个apk,假如我们现在用微信网页或者QQ网络连接了我们的apk软件,我们需要把一个视频传到电视上播放,这个时候是不是需要我们的apk作为服务端来接受文件了?这只是一个例子,可能还有局限性,更多的用处大家自己去发挥噢。

     

      在写博客之前我已经做了一个Android Http Server的开源项目,我把它取名叫AndServerAndServer源代码托管在Github:https://github.com/yanzhenjie/AndServerAndServerAndroid Http Server的简写,顾名思义AndServer是Android端Http服务器的一个项目。

     

      目的在于在Android可以很方便的搭建Http服务器,这几天有人问我局域网Client和Client通信的时候有时候用什么技术比较好,其实我想到的是Socket和Http,我们知道Http是基于Socket的,所以它是一个非常成熟的Socket,所以我选择了用Http来实现,今天的博客内容也是主要讲Android端如何搭建一个Http服务器。

     

    AndServer如何引用

     

      如果不想研究原理,只是想快速解决项目中的问题的同学,直接依赖AndServer,具体用法往下看,或者从Github上下载AndServer的Demo。这里给出AndroidStudio和Eclipse的使用方式:

     

    • Eclipse使用Jar包,如果需要依赖源码,请自行下载。

    • 下载Jar包

    • AndroidStudio使用Gradle构建添加依赖(推荐)

                         compile ‘com.yanzhenjie:andserver:1.0.1′

    Android端用什么技术实现Http服务器

     

      ApacheHttpCore是一个优秀的Http底层框架,支持构建服务器,支持构建客户端,所以我们第一个版本选择了Apache的HttpCore,因为Android弃用了ApacheHttpClient相关API,代码中会有弃用的警告,不过这一点大家不要担心,下面会给出解决方案。

     

    Android弃用了HttpClient后怎么继续使用HttpClient

     

      Android6.0之后SDK中删除HttpClient相关的API,我看了Google的官方文档后提示我们,如果还想继续使用HttpClient的话:

     

    方案一:AndroidStuid主module的gradle中配置:

    android {
         useLibrary ‘org.apache.http.legacy‘
    }

      

    如果提示编译不过的话,需要在android-sdk-windowsplatformsandroid-23optional下检查有没有以下两个文件:

     

    optional.json
    org.apache.http.legacy.jar

     

    如果你的SDK下没有org.apache.http.legacy.jar的话到这里下载

     

    方案二:如果你使用的是Eclipse

      

    拷贝android-sdk-windowsplatformsandroid-23optional下的org.apache.http.legacy.jar到你项目的libs下就完结。

     

    方案三:下载Apache的jar包(不推荐)

      

    从Apache官网下载HttpClientHttpCore的jar包导入到项目。地址是:http://hc.apache.org/downloads.cgi

      

    但是我推荐方案一和方案二,因为AndroidSDK中删除了HttpClient的api,但是手机系统里面还是有HttpClient的api的。方案一和二的原理最终都是引用SDK下android-sdk-windowsplatformsandroid-23optional下的org.apache.http.legacy.jar这个jar包到项目中,是Google处理过的jar,添加了AndroidHttpClient等适合Android使用的api,体积相对从Apache官网下载的jar小的多。

     

    如何使用AndServer

      

    这里先给大家看下AndServer怎么用,下一步详解如何一步步用HttpCore实现一个简易的HttpServer

     

    (一)实现AndServerRequestHandler接口,相当Servlet

      

    我们每写一个服务端接口,就要一个对应的类来处理,这里要实现AndServerRequestHandler接口,相当于Java继承Servet一样,我们只需要处理Request,在Response中给出响应即可:

     

    1
    2
    3
    4
    5
    6
    7
    public class AndServerTestHandler implements AndServerRequestHandler {
     
         @Override
         public void handle(HttpRequest rq, HttpResponse rp, HttpContext ct) throws HttpException, IOException {
              response.setEntity(new StringEntity("请求成功。""utf-8"));
         }
    }

    (二)在AndServer上注册接口名称,并启动服务器

      

    在启动AndServer的时候最好放在Service中,这里给出启动的关键代码。指定服务器的端口号,并注册接口,再启动服务器:

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    AndServerBuild andServerBuild = AndServerBuild.create();
    andServerBuild.setPort(4477);// 指定http端口号。
     
    // 注册接口。
     
    andServerBuild.add("test"new AndServerTestHandler());
    // 这里还可以注册很多接口。
     
    // 构建AndServer并启动服务器。
     
    AndServer andServer = andServerBuild.build();
    andServer.launch();

     

    到这里就完成了一个Android WebServer的搭建,我们已经可以通过浏览器或者NoHttp来访问我们的WebServer了。

     

    (三)其他设备如何访问

      

    如果是浏览器方法,和我们普通访问网站没有区别,比如访问我们上面的接口:

     

    Android本机访问的地址:http://locahost:4477/test
    局域网其他设备访问地址:http://192.168.1.116:4477/test

      

    如果是其它Android系统的设备,推荐使用NoHttp来访问,NoHttp是我的另一个Http客户端的项目,和AndWeb正好是相对的,一个做服务端,一个做客户端。

     

    到这里怎么用AndServer和Android搭建服务端的教程就完了,如果想自己尝试利用HttpCore搭建一个Http服务端的话,请往下看。

     

    AndroidCore实现一个简易的Http服务器

      

    其实里边的东西比较复杂个人感觉如果你不想自己写一个这样的框架的,没有太大必要看完,但是我推荐大家往下看噢,我相信你会有收获的。这里讲解下关键的代码,一共有六步:

     

    (一)ServerSocket构建服务端连接

      

    我们知道Http是基于Socket的,那么服务端肯定是ServerSocket了,所以我们这里也是需要一个ServerSocket来接受客户端请求的:

     

    1
    2
    3
    ServerSocket mServerSocket = new ServerSocket();
    mServerSocket.setReuseAddress(true);
    mServerSocket.bind(new InetSocketAddress(mPort));// 绑定端口

    (二)HttpProcessor增加Http协议处理器

      

    这个就是添加Http协议拦截器,都是Http基本信息。

     

    1
    2
    3
    4
    5
    6
    // HTTP协议拦截器。
    BasicHttpProcessor httpProcessor = new BasicHttpProcessor();
    httpProcessor.addInterceptor(new ResponseDate());
    httpProcessor.addInterceptor(new ResponseServer());
    httpProcessor.addInterceptor(new ResponseContent());
    httpProcessor.addInterceptor(new ResponseConnControl());

    (三)HttpParams初始化Http基本信息

      

    初始化Http连接的信息,比如超时时间,缓存区大小,是否使用GZIP等。

     

    1
    2
    3
    4
    5
    6
    7
    // HTTP Attribute.
    HttpParams httpParams = new BasicHttpParams();
    httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
              .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 1024)
              .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
              .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
              .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "WebServer/1.1");

    (四)HttpRequestHandlerRegistry增加接口名称

      

    这里要用HttpRequestHandlerRegistry把我们的RequestHandler注册进来,这一步也是最重要的,就是我们的接口名称,相当于是注册Servlet到web.xml中一样。

     

     

    举个例子,假设访问严振杰的主页http://www.yanzhenjie.com,我的主页下假设有一个login的接口,那我们的地址是:http://www.yanzhenjie.com/login,我们的Android Web Server也要实现这样一个可以访问的地址,就要注册一个login的接口,所以这里是增加接口名称:

     

    1
    2
    3
    4
    // 注册Http接口。
    HttpRequestHandlerRegistry handlerRegistry = new HttpRequestHandlerRegistry();
    handlerRegistry.register("/login"new RequestLoginHandle());// 增加登录接口
    handlerRegistry.register("/download"new RequestTestHandle());// 增加下载接口

      

    这里可以注册很多个接口,我们后面的接口对象是实现了HttpCoreHttpRequestHandler接口的自定义类,比如我们上面的RequestLoginHandle的实现是:

     

    1
    2
    3
    4
    5
    6
    7
    public class RequestLoginHandle implements HttpRequestHandler {
     
         @Override
         public void handle(HttpRequest rq, HttpResponse rp, HttpContext c) {
              // 只要在这里处理HttpRequest,如果要发出响应数据,用HttpResponse
         }
    }

    (五)HttpService创建Http服务

      

    前面准备的几步都是为这一步准备参数的,把我们前面准备好的httpProcessorhttpParamshandlerRegistry都传到HttpService,为下一步的Connection做好准备。

     

    1
    2
    3
    4
    // 创建HTTP服务。
    HttpService httpService = new HttpService(httpProcessor, new ConnectionReuseStrategy(), new HttpResponseFactory());
    httpService.setParams(httpParams);
    httpService.setHandlerResolver(handlerRegistry);

    (六)Socket、DefaultHttpServerConnection处理客户端请求

      

    上面的工作都做完了,就用到我们最开始准备好的ServerSocket来接受客户端的连接的socket了,接受到一个客户端的连接后,把刚才的httpParams和socket绑定到HttpServerConnection中,开始处理请求,下面代码中有一个RequestHandleTask类,这是一个单独的线程,因为每个请求都不能干涉服务器的主线程,所以这里新开一个非阻塞的线程去处理每一个请求:

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    while (isLoop) {
         if (!mServerSocket.isClosed()) {
              // 阻塞接受客户端。
              Socket socket = mServerSocket.accept();
              DefaultHttpServerConnection serverConnection = new DefaultHttpServerConnection();
              // 接受到一个请求到,把请求和刚才的param绑定到connection中。
              serverConnection.bind(socket, httpParams);
              // 开启一个线程去处理这个请求,不阻塞当前线程。
              RequestHandleTask requestTask = new RequestHandleTask(this, httpService, serverConnection);
              requestTask.setDaemon(true);
              AndWebUtil.executeRunnable(requestTask);
         }
    }

      

    RequestHandleTask中的run方法中,我们只要判断HttpServerConnection是连接的,就调用HttpServicehandleRequest方法交给HttpCore去分析请求,并自动分发到我们刚才注册的login接口中。

     

    1
    2
    3
    while (mServerConnection.isOpen()) {
         mHttpService.handleRequest(mServerConnection, new BasicHttpContext());
    }

      当HttpCore分析出来这个连接中的请求符合我们刚才注册的接口:

    1
    handlerRegistry.register("/login"new RequestLoginHandle());// 增加登录接口

      

    它会自动调用RequestLoginHandlehande()方法,因为我们实现了HttpRequestHandle接口。


    到这里,如何利用HttpCore搭建一个Android Http Server就完成了。

     

    把几个步骤合起来

      

    有的同学可能不会把上面的代码整合起来,这里给出完整的代码:

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    try {
         ServerSocket mServerSocket = new ServerSocket();
         mServerSocket.setReuseAddress(true);
         mServerSocket.bind(new InetSocketAddress(mPort));
         // HTTP协议拦截器。
         BasicHttpProcessor httpProcessor = new BasicHttpProcessor();
         httpProcessor.addInterceptor(new ResponseDate());
         httpProcessor.addInterceptor(new ResponseServer());
         httpProcessor.addInterceptor(new ResponseContent());
         httpProcessor.addInterceptor(new ResponseConnControl());
         // HTTP Attribute.
         HttpParams httpParams = new BasicHttpParams();
         httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout)
                   .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 1024)
                   .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
                   .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
                   .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "WebServer/1.1");
         // 注册Http接口。
         HttpRequestHandlerRegistry handlerRegistry = new HttpRequestHandlerRegistry();
         for (Map.Entry<String, AndServerRequestHandler> handlerEntry : mRequestHandlerMap.entrySet()) {
              handlerRegistry.register("/" + handlerEntry.getKey(), new DefaultHttpRequestHandler(handlerEntry.getValue()));
         }
         // 创建HTTP服务。
         HttpService httpService = new HttpService(httpProcessor, new DefaultConnectionReuseStrategy(), new DefaultHttpResponseFactory());
         httpService.setParams(httpParams);
         httpService.setHandlerResolver(handlerRegistry);
         /**
           * 开始接受客户端请求。
           */
         while (isLoop) {
              // 接收客户端套接字。
              if (!mServerSocket.isClosed()) {
                    Socket socket = mServerSocket.accept();
                    DefaultHttpServerConnection serverConnection = new DefaultHttpServerConnection();
                    serverConnection.bind(socket, httpParams);
                    // Dispatch request handler.
                    RequestHandleTask requestTask = new RequestHandleTask(this, httpService, serverConnection);
                    requestTask.setDaemon(true);
                    AndWebUtil.executeRunnable(requestTask);
              }
        }
    catch (Exception e) {
    finally {
         close();
    }

     

      RequestHandleTask类的完整代码:

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class RequestHandleTask extends Thread {
     
         private final HttpService mHttpService;
     
         private final HttpServerConnection mServerConnection;
     
         private DefaultAndServer mWebServerThread;
     
         public RequestHandleTask(DefaultAndServer webServerThread, HttpService httpservice, HttpServerConnection conn) {
              this.mWebServerThread = webServerThread;
              this.mHttpService = httpservice;
              this.mServerConnection = conn;
         }
     
         @Override
         public void run() {
              try {
                   while (mWebServerThread.isLooping() && mServerConnection.isOpen()) {
                        this.mHttpService.handleRequest(this.mServerConnection, new BasicHttpContext());
                   }
              catch (IOException e) {
              catch (HttpException e) {
              finally {
                    try {
                         this.mServerConnection.shutdown();
                    catch (IOException e) {
              }
          }
       }
    }

     

    好了咯,主要代码就是这些,剩下的自己去发挥吧!

  • 相关阅读:
    软件测试面试之剖析面试官
    DVWA之文件包含(File inclusion)
    DVWA之跨站请求伪造(CSRF)
    DVWA之命令注入(command injection)
    DVWA之Brute Force教程
    SQL注入中的整型注入实验
    模拟一次CSRF(跨站请求伪造)例子,适合新手
    安卓中Paint类和Canvas类的方法汇总
    C语言程序设计:现代方法(第2版)第三章全部习题答案
    C语言程序设计:现代方法(第2版)第二章全部习题答案
  • 原文地址:https://www.cnblogs.com/fengmin/p/5628806.html
Copyright © 2020-2023  润新知