您的位置:首页 > 移动开发 > Android开发

在android上如何编写一个小型web服务器

2017-07-04 22:59 591 查看
在android上如何编写一个小型web服务器?

这个是前几年之前接触到的一个项目的需求,需要是android手机建立一个无线热点,其他设备连接热点后,访问网站,都跳转到android手机上热点提供的网站,所以就需要android手机端实现一个简易的web服务器,服务器的资源文件都存储在sd卡,并且可以更新。

废话不多说,这边把项目早期做的可行性研究的demo整理了下,开源出来。

这个小型web服务器很大一部分参考AndroidWebServ工程(https://github.com/joinAero/AndroidWebServ),在此多谢。

刚刚百度了下,已经有很多大牛也实现了类似的功能,用的方法也基本一致。这边在大概介绍下用到的具体技术

1:创建一个ServerSocket

2:使用HttpService创建一个Http服务,并为这个http创建需要的HTTP请求执行器(用来响应客户端发过来的get,download,upload等请求)

3:接收通过ServerSocket过来的Socket请求,并将Socket映射到HttpService上,这样HttpService就会返回对应的数据到客户端,客户端就能浏览网页了

核心代码在org.join.ws.serv.WebServer上,这边摘取部分片段

// 创建服务器套接字
serverSocket = new ServerSocket(port);
// 设置端口重用
serverSocket.setReuseAddress(true);
// 创建HTTP协议处理器
BasicHttpProcessor httpproc = new BasicHttpProcessor();
// 增加HTTP协议拦截器
httpproc.addInterceptor(new ResponseDate());
httpproc.addInterceptor(new ResponseServer());
httpproc.addInterceptor(new ResponseContent());
httpproc.addInterceptor(new ResponseConnControl());
// 创建HTTP服务
HttpService httpService = new HttpService(httpproc,
new DefaultConnectionReuseStrategy(), new DefaultHttpResponseFactory());
// 创建HTTP参数
HttpParams params = new BasicHttpParams();
params.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");
// 设置HTTP参数
httpService.setParams(params);
// 创建HTTP请求执行器注册表
HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
// 增加HTTP请求执行器
reqistry.register(UrlPattern.DOWNLOAD, new HttpDownHandler(webRoot));
reqistry.register(UrlPattern.DELETE, new HttpDelHandler(webRoot));
reqistry.register(UrlPattern.UPLOAD, new HttpUpHandler(webRoot));
reqistry.register(UrlPattern.PROGRESS, new HttpProgressHandler());
reqistry.register(UrlPattern.BROWSE, new HttpFBHandler(webRoot));
// 设置HTTP请求执行器
httpService.setHandlerResolver(reqistry);
// 回调通知服务开始
if (mListener != null) {
mListener.onStarted();
}
/* 循环接收各客户端 */
isLoop = true;
while (isLoop && !Thread.interrupted()) {
// 接收客户端套接字
Socket socket = serverSocket.accept();
// 绑定至服务器端HTTP连接
DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
conn.bind(socket, params);
// 派送至WorkerThread处理请求
Thread t = new WorkerThread(httpService, conn, mListener);
t.setDaemon(true); // 设为守护线程
pool.execute(t); // 执行
}

项目中还用到 一个比较重要的库,Jangod,这个库很强大,可以用来从文件或者从代码中加载html,并支持css,有兴趣的同学可以 继续研究下去。
另外还有一个问题,怎么样把客户端所有的http请求都引到我们的ServerSocket?这边就需要用到nat表了,前提是你手机root了

1:将所有对80端口的http请求都强制转换到我们的ServerSocket,命令如下

String cmd = "iptables -t nat -A PREROUTING -d 0.0.0.0/0 -p tcp --dport 80 -j DNAT --to-destination "
+CommonUtil.getLocalIpAddress()+":" + Config.PORT;//192.168.43.1:7766";

2:将 所有对53端口的dns请求都强制装换到我们的dns请求端口,并实现dns的响应
String cmd53 = "iptables -t nat -A PREROUTING -d 0.0.0.0/0 -p udp --dport 53 -j DNAT --to-destination "
+CommonUtil.getLocalIpAddress()+":" + Config.PORT_DNS;//192.168.43.1:7766";
package org.join.ws.serv;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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.BasicHttpProcessor;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.join.ws.Constants.Config;
import org.join.ws.serv.req.HttpDelHandler;
import org.join.ws.serv.req.HttpDownHandler;
import org.join.ws.serv.req.HttpFBHandler;
import org.join.ws.serv.req.HttpProgressHandler;
import org.join.ws.serv.req.HttpUpHandler;
import org.join.ws.util.CommonUtil;

/**
* @brief DnsWeb服务类
* @author talkercenter
*/
public class DnsServer extends Thread {

static final String TAG = "DnsServer";
static final boolean DEBUG = false || Config.DEV_MODE;

byte[] requestBuffer = new byte[256];
byte[] responseBuffer = new byte[256];
byte[] ipBuffer = { (byte) 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x7c, 0x00, 0x04, (byte)0xc0, (byte)0xa8, 0x2b, 0x01 };

public static final int ERR_UNEXPECT = 0x0101;
public static final int ERR_PORT_IN_USE = 0x0102;
public static final int ERR_TEMP_NOT_FOUND = 0x0103;

private int port;
//private String webRoot;

private DatagramSocket serverSocket;
/* package */static boolean isLoop;

private OnWebServListener mListener;

//private ExecutorService pool; // 线程池

public DnsServer(int port, final String webRoot) {
super();
this.port = port;
//this.webRoot = webRoot;
isLoop = false;

//pool = Executors.newCachedThreadPool();
}

@Override
public void run() {
try {
// Decide if port is in use.
if (CommonUtil.getSingleton().isLocalPortInUse(port)) {
if (mListener != null) {
mListener.onError(ERR_PORT_IN_USE);
}
return;
}
// 创建服务器套接字
serverSocket = new Dat
4000
agramSocket(port);
DatagramPacket requestPacket = new DatagramPacket(requestBuffer,requestBuffer.length);
/* 循环接收各客户端 */
isLoop = true;
while (isLoop && !Thread.interrupted()) {
serverSocket.receive(requestPacket);
int requestLength = requestPacket.getLength();
System.arraycopy(requestBuffer, 0, responseBuffer, 0, requestLength);
System.arraycopy(ipBuffer, 0, responseBuffer, requestLength, ipBuffer.length);
// 标志位
responseBuffer[2] = (byte) 0x81;
responseBuffer[3] = (byte) 0x80;
// 响应数
responseBuffer[6] = (byte) 0x00;
responseBuffer[7] = (byte) 0x01;
DatagramPacket response = new DatagramPacket(responseBuffer, requestLength + ipBuffer.length, requestPacket.getAddress(), requestPacket.getPort());
serverSocket.send(response);
}
} catch (IOException e) {
if (isLoop) { // 以排除close造成的异常
// 回调通知服务出错
if (mListener != null) {
mListener.onError(ERR_UNEXPECT);
}
if (DEBUG)
e.printStackTrace();
isLoop = false;
}
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
// 回调通知服务结束
if (mListener != null) {
mListener.onStopped();
}
} catch (Exception e) {
}
}
}

public void close() {
isLoop = false;
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (Exception e) {
}
}

public interface OnWebServListener {
void onStarted();

void onStopped();

void onError(int code);
}

public void setOnWebServListener(OnWebServListener mListener) {
this.mListener = mListener;
}

}


结束,完整代码请访问
https://github.com/bobohuang1985/android-utils-api


欢迎大家提出意见。大家可以通过QQ群,或者微信公众号交流:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐