这几天调查了一下在android设备上实现HTTP服务的功能,发现了NanoHTTPD,能通过java实现HTTP的功能。
https://github.com/NanoHttpd/nanohttpd
自己整了个小“栗子”,大家一起品尝学习一下,实现了从PC端浏览android设备上文件的功能。HTTP是在TCP/IP协议之上的应用层协议,咱们都做过JAVA的网络编程(java socket),再了解一下HTTP协议的规则,就很容易理解NanoHTTPD的实现原理。
一、几个关键的类
1、NanoHTTPD.java
(1)、在构造方法中指定服务端的端口号,并生成ServerSocket对象
(2)、在构造方法中有特别关键的一段,serve是抽象方法,由咱们在应用层重写实现。
// creates a default handler that redirects to deprecated serve();
this.httpHandler = new IHandler() {
@Override
public Response handle(IHTTPSession input) {
return NanoHTTPD.this.serve(input);
}
};
public Response handle(IHTTPSession session) {
for (IHandler interceptor : interceptors) {
Response response = interceptor.handle(session);
if (response != null)
return response;
}
return httpHandler.handle(session); // 这里调用上边的serve方法
}
(3)、在start()方法中生成一个ServerRunnable对象在子线程中运行
2、ServerRunnable.java(implements Runnable) // 用于在子线程中执行
(1)、在run()方法中,循环利用NanoHTTPD.start()中生成的ServerSocket对象来监听客户端的连接
Socket finalAccept = httpd.getMyServerSocket().accept();
这是很关键的一步,每建立一个连接就生成一个Socket对象。
(2)、利用finalAccept以及inputStream(inputStream = finalAccept.getInputStream())生成ClientHandler对象,并在子线程中执行。 也就是说每建立一个连接,就会生成一个Socket对象,然后对应建立一个子线程去处理。
3、ClientHandler.java(implements Runnable) // 用于在子线程中执行
在run方法中封装HttpSession,执行HttpSession的execute()方法
HTTPSession session = new HTTPSession(httpd, tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress());
session.execute();
4、HTTPSession.java
根据HTTP协议进行解析,获得URI,METHOD,PARAMS,HEADERS,COOKIES等
注意execute()方法中的最后一句:
r = httpd.handle(this); // r是Response对象,这里就关联到了1里面的handle() – > serve()方法
二、使用方法:
1、继承 ‘NanoHTTPD’ , 复写’serve()’
2、构造方法中指明端口号
3、调用NanoHTTPD的start方法,开启HTTP服务
4、主要的代码实现
package com.example.nanohttpd;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.List;
import java.util.Map;
import org.nanohttpd.protocols.http.IHTTPSession;
import org.nanohttpd.protocols.http.NanoHTTPD;
import org.nanohttpd.protocols.http.response.Response;
import org.nanohttpd.protocols.http.response.Status;
import android.text.TextUtils;
import android.util.Log;
/**
*
* @author lixm
*
*/
public class HttpServerImpl extends NanoHTTPD {
// http://172.22.158.31:8080/getFileList?dirPath=/sdcard
// http://172.22.158.31:8080/getFile?fileName=/sdcard/FaceFingerMatch_AD
public static final int DEFAULT_SERVER_PORT = 8080;
public static final String TAG = "lixm";
private static final String REQUEST_ROOT = "/";
private static final String REQUEST_TEST = "/test";
private static final String REQUEST_ACTION_GET_FILE = "/getFile";
private static final String REQUEST_ACTION_GET_FILE_LIST = "/getFileList";
public HttpServerImpl() {
super(DEFAULT_SERVER_PORT);
}
@Override
public Response serve(IHTTPSession session) {
String strUri = session.getUri();
String method = session.getMethod().name();
Log.d(TAG,"Response serve uri = " + strUri + ", method = " + method);
if(REQUEST_ROOT.equals(strUri)) { // 根目录
return responseRootPage(session);
}else if(REQUEST_TEST.equals(strUri)){ // 返回给调用端json串
return responseJson();
}else if(REQUEST_ACTION_GET_FILE_LIST.equals(strUri)){ // 获取文件列表
Map params = session.getParms();
String dirPath = params.get("dirPath");
if(!TextUtils.isEmpty(dirPath)){
return responseFileList(session,dirPath);
}
}else if(REQUEST_ACTION_GET_FILE.equals(strUri)){ // 下载文件
Map params = session.getParms();
// 下载的文件名称
String fileName = params.get("fileName");
File file = new File(fileName);
if(file.exists()){
if(file.isDirectory()){
return responseFileList(session,fileName);
}else{
return responseFileStream(session,fileName);
}
}
}
return response404(session);
}
private Response responseRootPage(IHTTPSession session) {
StringBuilder builder = new StringBuilder();
builder.append("");
builder.append("这是lixm的测试! \n");
builder.append("\n");
//return Response.newFixedLengthResponse(Status.OK, "application/octet-stream", builder.toString());
return Response.newFixedLengthResponse(builder.toString());
}
/**
* 返回给调用端LOG日志文件
* @param session
* @return
*/
private Response responseFileStream(IHTTPSession session,String filePath) {
Log.d("lixm", "responseFileStream() ,fileName = " + filePath);
try {
FileInputStream fis = new FileInputStream(filePath);
//application/octet-stream
return Response.newChunkedResponse(Status.OK, "application/octet-stream", fis);
}
catch (FileNotFoundException e) {
Log.d("lixm", "responseFileStream FileNotFoundException :" ,e);
return response404(session);
}
}
/**
*
* @param session http请求
* @param dirPath 文件夹路径名称
* @return
*/
private Response responseFileList(IHTTPSession session,String dirPath) {
Log.d("lixm", "responseFileList() , dirPath = " + dirPath);
List fileList = FileUtils.getFilePaths(dirPath, false);
StringBuilder sb = new StringBuilder();
for(String filePath : fileList){
sb.append("" + filePath + "" + "
");
}
return Response.newFixedLengthResponse(sb.toString());
}
/**
* 调用的路径出错
* @param session
* @param url
* @return
*/
private Response response404(IHTTPSession session) {
String url = session.getUri();
StringBuilder builder = new StringBuilder();
builder.append("");
builder.append("Sorry, Can't Found "+url + " !");
builder.append("\n");
return Response.newFixedLengthResponse(builder.toString());
}
/**
* 返回给调用端json字符串
* @return
*/
private Response responseJson(){
return Response.newFixedLengthResponse("调用成功");
}
}
三、示例
1、示例实现了在PC上通过浏览器浏览android设备SD卡上所有文件的功能,能够打开和下载文件
2、在PC端浏览器输入http://ip:8080/getFileList?dirPath=/sdcard
3、示例代码:http://download.csdn.net/detail/dami_lixm/9882660(这个下载分太多了,用下面的链接下载)
https://download.csdn.net/download/dami_lixm/11341046 (这个下载分少)