前言
在写博客之前我已经做了一个Android Http Server
的开源项目,我把它取名叫AndServer
,AndServer
源代码托管在Github:https://github.com/yanzhenjie/AndServer。AndServer
是Android 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包,如果需要依赖源码,请自行下载。
-
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
的话:
如果你的SDK下没有
org.apache.http.legacy.jar
的话到这里下载。
方案一:AndroidStuid主module的gradle中配置:
android {
useLibrary ‘org.apache.http.legacy‘
}
如果提示编译不过的话,需要在android-sdk-windows\platforms\android-23\optional
下检查有没有以下两个文件:
optional.json
org.apache.http.legacy.jar
方案二:如果你使用的是Eclipse
拷贝android-sdk-windows\platforms\android-23\optional
下的org.apache.http.legacy.jar到你项目的libs下就完结。
方案三:下载Apache的jar包(不推荐)
从Apache官网下载HttpClient
和HttpCore
的jar包导入到项目。地址是:http://hc.apache.org/downloads.cgi。
但是我推荐方案一和方案二,因为AndroidSDK中删除了HttpClient
的api,但是手机系统里面还是有HttpClient
的api的。方案一和二的原理最终都是引用SDK下android-sdk-windows\platforms\android-23\optional
下的org.apache.http.legacy.jar
这个jar包到项目中,是Google处理过的jar,添加了AndroidHttpClient
等适合Android使用的api,体积相对从Apache官网下载的jar小的多。
如何使用AndServer
这里先给大家看下AndServer怎么用,下一步详解如何一步步用HttpCore
实现一个简易的HttpServer
。
(一)实现AndServerRequestHandler接口,相当Servlet
我们每写一个服务端接口,就要一个对应的类来处理,这里要实现AndServerRequestHandler
接口,相当于Java继承Servet一样,我们只需要处理Request,在Response中给出响应即可:
publicclassAndServerTestHandlerimplementsAndServerRequestHandler{
@Override
publicvoid handle(HttpRequest rq,HttpResponse rp,HttpContext ct)throwsHttpException,IOException{
response.setEntity(newStringEntity("请求成功。","utf-8"));
}
}
(二)在AndServer上注册接口名称,并启动服务器
在启动AndServer
的时候最好放在Service中,这里给出启动的关键代码。指定服务器的端口号,并注册接口,再启动服务器:
AndServerBuild andServerBuild =AndServerBuild.create();
andServerBuild.setPort(4477);// 指定http端口号。
// 注册接口。
andServerBuild.add("test",newAndServerTestHandler());
// 这里还可以注册很多接口。
// 构建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
来接受客户端请求的:
ServerSocket mServerSocket =newServerSocket();
mServerSocket.setReuseAddress(true);
mServerSocket.bind(newInetSocketAddress(mPort));// 绑定端口
(二)HttpProcessor增加Http协议处理器
这个就是添加Http协议拦截器,都是Http基本信息。
// HTTP协议拦截器。
BasicHttpProcessor httpProcessor =newBasicHttpProcessor();
httpProcessor.addInterceptor(newResponseDate());
httpProcessor.addInterceptor(newResponseServer());
httpProcessor.addInterceptor(newResponseContent());
httpProcessor.addInterceptor(newResponseConnControl());
(三)HttpParams初始化Http基本信息
初始化Http连接的信息,比如超时时间,缓存区大小,是否使用GZIP等。
// HTTP Attribute.
HttpParams httpParams =newBasicHttpParams();
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的接口,所以这里是增加接口名称:
// 注册Http接口。
HttpRequestHandlerRegistry handlerRegistry =newHttpRequestHandlerRegistry();
handlerRegistry.register("/login",newRequestLoginHandle());// 增加登录接口
handlerRegistry.register("/download",newRequestTestHandle());// 增加下载接口
这里可以注册很多个接口,我们后面的接口对象是实现了HttpCore
中HttpRequestHandler
接口的自定义类,比如我们上面的RequestLoginHandle
的实现是:
publicclassRequestLoginHandleimplementsHttpRequestHandler{
@Override
publicvoid handle(HttpRequest rq,HttpResponse rp,HttpContext c){
// 只要在这里处理HttpRequest,如果要发出响应数据,用HttpResponse
}
}
(五)HttpService创建Http服务
前面准备的几步都是为这一步准备参数的,把我们前面准备好的httpProcessor
、httpParams
、handlerRegistry
都传到HttpService
,为下一步的Connection做好准备。
// 创建HTTP服务。
HttpService httpService =newHttpService(httpProcessor,newConnectionReuseStrategy(),newHttpResponseFactory());
httpService.setParams(httpParams);
httpService.setHandlerResolver(handlerRegistry);
(六)Socket、DefaultHttpServerConnection处理客户端请求
上面的工作都做完了,就用到我们最开始准备好的ServerSocket
来接受客户端的连接的socket了,接受到一个客户端的连接后,把刚才的httpParams和socket绑定到HttpServerConnection
中,开始处理请求,下面代码中有一个RequestHandleTask
类,这是一个单独的线程,因为每个请求都不能干涉服务器的主线程,所以这里新开一个非阻塞的线程去处理每一个请求:
while(isLoop){
if(!mServerSocket.isClosed()){
// 阻塞接受客户端。
Socket socket = mServerSocket.accept();
DefaultHttpServerConnection serverConnection =newDefaultHttpServerConnection();
// 接受到一个请求到,把请求和刚才的param绑定到connection中。
serverConnection.bind(socket, httpParams);
// 开启一个线程去处理这个请求,不阻塞当前线程。
RequestHandleTask requestTask =newRequestHandleTask(this, httpService, serverConnection);
requestTask.setDaemon(true);
AndWebUtil.executeRunnable(requestTask);
}
}
在RequestHandleTask
中的run方法中,我们只要判断HttpServerConnection
是连接的,就调用HttpService
的handleRequest
方法交给HttpCore
去分析请求,并自动分发到我们刚才注册的login接口中。
while(mServerConnection.isOpen()){
mHttpService.handleRequest(mServerConnection,newBasicHttpContext());
}
当HttpCore
分析出来这个连接中的请求符合我们刚才注册的接口:
handlerRegistry.register("/login",newRequestLoginHandle());// 增加登录接口
它会自动调用RequestLoginHandle
的hande()
方法,因为我们实现了HttpRequestHandle
接口。
到这里,如何利用HttpCore
搭建一个Android Http Server
就完成了。
把几个步骤合起来
有的同学可能不会把上面的代码整合起来,这里给出完整的代码:
try{
ServerSocket mServerSocket =newServerSocket();
mServerSocket.setReuseAddress(true);
mServerSocket.bind(newInetSocketAddress(mPort));
// HTTP协议拦截器。
BasicHttpProcessor httpProcessor =newBasicHttpProcessor();
httpProcessor.addInterceptor(newResponseDate());
httpProcessor.addInterceptor(newResponseServer());
httpProcessor.addInterceptor(newResponseContent());
httpProcessor.addInterceptor(newResponseConnControl());
// HTTP Attribute.
HttpParams httpParams =newBasicHttpParams();
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 =newHttpRequestHandlerRegistry();
for(Map.Entry<String,AndServerRequestHandler> handlerEntry : mRequestHandlerMap.entrySet()){
handlerRegistry.register("/"+ handlerEntry.getKey(),newDefaultHttpRequestHandler(handlerEntry.getValue()));
}
// 创建HTTP服务。
HttpService httpService =newHttpService(httpProcessor,newDefaultConnectionReuseStrategy(),newDefaultHttpResponseFactory());
httpService.setParams(httpParams);
httpService.setHandlerResolver(handlerRegistry);
/**
* 开始接受客户端请求。
*/
while(isLoop){
// 接收客户端套接字。
if(!mServerSocket.isClosed()){
Socket socket = mServerSocket.accept();
DefaultHttpServerConnection serverConnection =newDefaultHttpServerConnection();
serverConnection.bind(socket, httpParams);
// Dispatch request handler.
RequestHandleTask requestTask =newRequestHandleTask(this, httpService, serverConnection);
requestTask.setDaemon(true);
AndWebUtil.executeRunnable(requestTask);
}
}
}catch(Exception e){
}finally{
close();
}
RequestHandleTask
类的完整代码:
publicclassRequestHandleTaskextendsThread{
privatefinalHttpService mHttpService;
privatefinalHttpServerConnection mServerConnection;
privateDefaultAndServer mWebServerThread;
publicRequestHandleTask(DefaultAndServer webServerThread,HttpService httpservice,HttpServerConnection conn){
this.mWebServerThread = webServerThread;
this.mHttpService = httpservice;
this.mServerConnection = conn;
}
@Override
publicvoid run(){
try{
while(mWebServerThread.isLooping()&& mServerConnection.isOpen()){
this.mHttpService.handleRequest(this.mServerConnection,newBasicHttpContext());
}
}catch(IOException e){
}catch(HttpException e){
}finally{
try{
this.mServerConnection.shutdown();
}catch(IOException e){
}
}
}
}