原创文章,转载请注明出处:NanoHttpd 安卓HTTP Sever建立:http://www.jianshu.com/p/ef6279a429d4
前言
近来,接到了一个任务,要在手机搭建一个HTTP Server ,用于文件的分享。即手机端的应用启动以后,电脑可以通过局域网的手机ip地址连接上手机,并且下载手机分享的文件。功能很方便,并且由于是http server的关系,最后应该能实现多设备连接到同一部手机上下载文件。同时,技术利用局域网的wifi或者手机热点来实现,这就使得传输效率非常高(突然想到,后面应该增加一个功能,使没有安装该软件的安卓手机可以使用手机浏览器连接该http server下载该软件),因为先是练手,并且也可以先了解建立http server的流程,对http协议、http server与客户端的交互进行分析,故没有采用data binding等设计模式相关的内容,避免被无关因素干扰了程序的开发过程。
HTTP协议
TCP/IP协议的内容无需多言,读者可以自行了解。http是客户端与服务器之间发起请求和应答的标准,服务器根据客户端发送的不同的http请求并进行处理,处理后向客户端发送应答消息。它基于TCP/IP协议来传递各式各样的数据。
在编写http server的过程中,也许你也会有如此疑问,http不过是一个协议罢了,如果我们使用自定义的http server(或许只需叫做server)来传递数据,同时使用自定义的http client(同理,只需叫client)来进行客户端和服务器端的交互以及数据的传输,我们其实并不需要使用http协议,我们可以写个安卓程序,使用socket和serversocket的程序,用不同的设备运行这个程序,利用我们自己定好的标识符即可实现交互和传输了。认真想一想,我们的设备难道必须要安装这个程序才可以运行使用吗,并且对于pc,无法安装该安卓程序,我们所要实现的数据传输不就无法实现了嘛?这样想一想,我们就应该使用已经成熟的,大家公认可用并且获得普遍支持的协议来实现应用,这样我们的应用才更加的具有普遍性,对不同的设备具有同样的兼容性。
有些偏题了,现在再回到http协议的内容:
http协议有如下几个特点:
- 无连接:处理完请求并且得到应答后即断开链接
- 媒体独立:任何类型的数据都可通过http协议完成
- 无状态:不记录前面的状态信息
从这些特点可以看出http协议具有传输速度快,应答速度快,传输方便等特点。
下面再看看http协议客户端发送请求的格式:
图上的描述已经足够清晰了,还有服务器发送响应的格式:
http协议中我们最常用到的请求就是get和post,所以我们的http server基本上实现这两个方法即可实现文件的上传和下载了。其中get请求通常用于请求指定的页面信息并返回实体主体。post请求用于提交表单或者上传文件。其他的请求方法网络上有详细内容。http的相应头和状态码等内容网络上有更加详细的内容了,这里也就不再赘述。
http服务器
既然要在安卓中建立一个http服务器,那么采用java来建立即可,基本的思路就是建立java的serversocket来等待连接,连接后服务器不断接受客户端的数据并且进行处理,由于采用的http的协议,对数据的处理及回复也需采用http协议的内容。从零构建一个http的服务器具有一定的难度,要简化处理就先利用nanohttpd来构建服务器。
前期准备
- 先在官网上将nanohttpd下载到本地,解压缩以后进入文件夹,使用mvn compile和 man package(我的电脑是Linux)将自动编译构建jar文件,jar文件在core文件夹下的target文件夹内。
- android studio中新建工程,在工程的依赖关系中引入jar包,引入的jar包就可用于构建http server,基本就是建立一个新的类,继承于NanoHTTPD
正式开始
主界面
主界面布局简单,简单描述一下即可,不贴代码了。主界面使用了一个TextView,该TextView用于启动应用后显示本机的ip地址和自定的http server的端口号,其他主机可以使用该ip地址和端口号进行远程连接。
//获取IP地址
public static String getLocalIpStr(Context context){
WifiManager wifiManager=(WifiManager)context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo=wifiManager.getConnectionInfo();
return intToIpAddr(wifiInfo.getIpAddress());
}
private static String intToIpAddr(int ip){
return (ip & 0xFF)+"."
+ ((ip>>8)&0xFF) + "."
+ ((ip>>16)&0xFF) + "."
+ ((ip>>24)&0xFF);
}
HTTP Server
这里我们开始正式的构建http server ,首先建立一个服务器类继承于NanoHTTPD,实现NanoHTTPD中的基本方法就可以完成我们所要的操作。
public class FileServer extends NanoHTTPD{
public static final int DEFAULT_SERVER_PORT= com.example.zjt.nanohttpexample.Status.MY_PORT;//为8080
public static final String TAG = FileServer.class.getSimpleName();
//根目录
private static final String REQUEST_ROOT = "/";
private List fileList;//用于分享的文件列表
public FileServer(List fileList){
super(DEFAULT_SERVER_PORT);
this.fileList = fileList;
}
//当接受到连接时会调用此方法
public Response serve(IHTTPSession session){
if(REQUEST_ROOT.equals(session.getUri())||session.getUri().equals("")){
return responseRootPage(session);
}
return responseFile(session);
}
//对于请求根目录的,返回分享的文件列表
public Response responseRootPage(IHTTPSession session){
StringBuilder builder = new StringBuilder();
builder.append("");
builder.append("");
for(int i = 0 , len = fileList.size(); i < len ; i++){
File file = new File(fileList.get(i).getPath());
if(file.exists()){
//文件及下载文件的链接,定义了一个文件类,这里使用getPath方法获得路径,使用getName方法获得文件名
builder.append("- "+file.getName()+"
");
}
}
builder.append("- 分享文件数量: "+fileList.size()+"
");
builder.append("
");
builder.append("\n");
//回送应答
return Response.newFixedLengthResponse(String.valueOf(builder));
}
//对于请求文件的,返回下载的文件
public Response responseFile(IHTTPSession session){
try {
//uri:用于标示文件资源的字符串,这里即是文件路径
String uri = session.getUri();
//文件输入流
FileInputStream fis = new FileInputStream(uri);
// 返回OK,同时传送文件,为了安全这里应该再加一个处理,即判断这个文件是否是我们所分享的文件,避免客户端访问了其他个人文件
return Response.newFixedLengthResponse(Status.OK,"application/octet-stream",fis,fis.available());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response404(session,null);
}
//页面不存在,或者文件不存在时
public Response response404(IHTTPSession session,String url) {
StringBuilder builder = new StringBuilder();
builder.append("body>");
builder.append("Sorry,Can't Found" + url + " !");
builder.append("