Android之利用NanoHttpd搭建伺服器
NanoHttpd是一個開源庫,使用Java實現,可以在非常方便地整合到Android應用中去,實現了一個輕量級的 Web Server。
專案地址: https://github.com/NanoHttpd/nanohttpd
使用方法:在下面下載連結中下載nanohttpd-2.2.0.jar匯入到Android專案中的libs目錄下。
https://github.com/NanoHttpd/nanohttpd/releases
在介紹使用NanoHttpd實現簡易WebServer之前,我們首先熟悉下區域網Socket通訊。一個Client工程,程式碼地址為https://github.com/jltxgcy/AppVulnerability/tree/master/MyClient。一個Server工程,程式碼地址為https://github.com/jltxgcy/AppVulnerability/tree/master/MyServer。
兩個工程要在要同樣的Wifi環境下,MyClient工程要修改連線目標的IP地址。如下:
clientSocket = new Socket("10.10.154.74",6100);
這個IP地址可以通過設定->關於手機->狀態資訊->IP地址獲取。如下圖:
具體的程式碼就不介紹了,大家自己分析。
0x01
下面介紹使用NanoHttpd實現簡易WebServer。程式碼地址為https://github.com/jltxgcy/AppVulnerability/tree/master/NanoHttpD。
執行NanoHttpD後,在本機的UC瀏覽器輸入http://127.0.0.1:8088,會返回it works。在其他連線相同wifi的手機瀏覽器上輸入http://10.10.154.12(也就是執行NanoHttpD的手機IP),也會出現it works。
那麼這個本地webServer是什麼原理呢?
我們先看主Activity,程式碼如下:
public class MainActivity extends Activity {
private SimpleServer server;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
server = new SimpleServer();
try {
// 因為程式模擬的是html放置在asset目錄下,
// 所以在這裡儲存一下AssetManager的指標。
server.asset_mgr = this.getAssets();
// 啟動web服務
server.start();
Log.i("Httpd", "The server started.");
} catch(IOException ioe) {
Log.w("Httpd", "The server could not start.");
}
}
......
}
建立了SimpleServer物件,然後呼叫了它的start方法。我們來看SimpleServer類的程式碼:
public class SimpleServer extends NanoHTTPD {
AssetManager asset_mgr;
public SimpleServer() {
// 埠是8088,也就是說要通過http://127.0.0.1:8088來訪當問
super(8088);
}
public Response serve(String uri, Method method,
Map<String, String> header,
Map<String, String> parameters,
Map<String, String> files)
{
int len = 0;
byte[] buffer = null;
Log.d("jltxgcy", header.get("remote-addr"));
// 預設傳入的url是以“/”開頭的,需要刪除掉,否則就變成了絕對路徑
String file_name = uri.substring(1);
// 預設的頁面名稱設定為index.html
if(file_name.equalsIgnoreCase("")){
file_name = "index.html";
}
try {
//通過AssetManager直接開啟檔案進行讀取操作
InputStream in = asset_mgr.open(file_name, AssetManager.ACCESS_BUFFER);
//假設單個網頁檔案大小的上限是1MB
buffer = new byte[1024*1024];
int temp=0;
while((temp=in.read())!=-1){
buffer[len]=(byte)temp;
len++;
}
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 將讀取到的檔案內容返回給瀏覽器
return new NanoHTTPD.Response(new String(buffer,0,len));
}
}
SimpleServer繼承了NanoHTTPD,server.start()實際上呼叫NanoHTTPD類的start方法。如下:
public void start() throws IOException {
myServerSocket = new ServerSocket();
myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
myThread = new Thread(new Runnable() {
@Override
public void run() {
do {
try {
final Socket finalAccept = myServerSocket.accept();
registerConnection(finalAccept);
finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
final InputStream inputStream = finalAccept.getInputStream();
if (inputStream == null) {
safeClose(finalAccept);
unRegisterConnection(finalAccept);
} else {
asyncRunner.exec(new Runnable() {
@Override
public void run() {
OutputStream outputStream = null;
try {
outputStream = finalAccept.getOutputStream();
TempFileManager tempFileManager = tempFileManagerFactory.create();
HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
while (!finalAccept.isClosed()) {
session.execute();
}
} catch (Exception e) {
// When the socket is closed by the client, we throw our own SocketException
// to break the "keep alive" loop above.
if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
e.printStackTrace();
}
} finally {
safeClose(outputStream);
safeClose(inputStream);
safeClose(finalAccept);
unRegisterConnection(finalAccept);
}
}
});
}
} catch (IOException e) {
}
} while (!myServerSocket.isClosed());
}
});
myThread.setDaemon(true);
myThread.setName("NanoHttpd Main Listener");
myThread.start();
}
建立了一個Socket Server,myServerSocket.accept()阻塞等待連線,當在本機瀏覽器輸入http://127.0.0.1:8088,建立連線,接下來去處理這個連線,myThread執行緒會繼續執行到session.execute。我們來看那這個函式的程式碼:
@Override
public void execute() throws IOException {
try {
// Read the first 8192 bytes.
// The full header should fit in here.
// Apache's default header limit is 8KB.
// Do NOT assume that a single read will get the entire header at once!
byte[] buf = new byte[BUFSIZE];
splitbyte = 0;
rlen = 0;
{
int read = -1;
try {
read = inputStream.read(buf, 0, BUFSIZE);
} catch (Exception e) {
safeClose(inputStream);
safeClose(outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
if (read == -1) {
// socket was been closed
safeClose(inputStream);
safeClose(outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
while (read > 0) {
rlen += read;
splitbyte = findHeaderEnd(buf, rlen);
if (splitbyte > 0)
break;
read = inputStream.read(buf, rlen, BUFSIZE - rlen);
}
}
if (splitbyte < rlen) {
ByteArrayInputStream splitInputStream = new ByteArrayInputStream(buf, splitbyte, rlen - splitbyte);
SequenceInputStream sequenceInputStream = new SequenceInputStream(splitInputStream, inputStream);
inputStream = sequenceInputStream;
}
parms = new HashMap<String, String>();
if(null == headers) {
headers = new HashMap<String, String>();
}
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
// Decode the header into parms and header java properties
Map<String, String> pre = new HashMap<String, String>();
decodeHeader(hin, pre, parms, headers);
method = Method.lookup(pre.get("method"));
if (method == null) {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error.");
}
uri = pre.get("uri");
cookies = new CookieHandler(headers);
// Ok, now do the serve()
Response r = serve(this);
if (r == null) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
} else {
cookies.unloadQueue(r);
r.setRequestMethod(method);
r.send(outputStream);
}
} catch (SocketException e) {
// throw it out to close socket object (finalAccept)
throw e;
} catch (SocketTimeoutException ste) {
throw ste;
} catch (IOException ioe) {
Response r = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
r.send(outputStream);
safeClose(outputStream);
} catch (ResponseException re) {
Response r = new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
r.send(outputStream);
safeClose(outputStream);
} finally {
tempFileManager.clear();
}
}
這個函式解析http://127.0.0.1:8088(資料來源於finalAccept.getInputStream()),然後呼叫了SimpleServer的serve方法,這個server方法返回的就是顯示在瀏覽器中的內容。
我們根據除錯,看一下public Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parameters, Map<String, String> files),這些引數返回的值到底是多少?
url為/,method為GET,head為{accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,UC/145,plugin/1,alipay/un, accept-encoding=gzip, host=127.0.0.1:8088, accept-language=zh-CN, http-client-ip=127.0.0.1, cache-control=max-age=0, x-ucbrowser-ua=dv(Nexus 6);pr(UCBrowser/10.7.0.634);ov(Android 5.1.1);ss(411*683);pi(1440*2392);bt(UM);pm(1);bv(1);nm(0);im(0);sr(0);nt(2);, remote-addr=127.0.0.1, user-agent=Mozilla/5.0 (Linux; U; Android 5.1.1; zh-CN; Nexus 6 Build/LMY47Z) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/10.7.0.634 U3/0.8.0 Mobile Safari/534.30, connection=keep-alive},parameters為{NanoHttpd.QUERY_STRING=null},files為{}。
如果請求的地址為http://127.0.0.1:8088/adv?d=1,則url為adv,parameter為{d=1, NanoHttpd.QUERY_STRING=d=1}。
---------------------
作者:jltxgcy
來源:CSDN
原文:https://blog.csdn.net/jltxgcy/article/details/50680394
版權宣告:本文為博主原創文章,轉載請附上博文連結!
相關文章
- Android開發利用NanoHttpd搭建伺服器AndroidNaNhttpd伺服器
- Android 使用NanoHTTPD框架搭建web的後端伺服器(一)AndroidNaNhttpd框架Web後端伺服器
- 基於 NanoHttpd 的 Android 視訊伺服器開發NaNhttpdAndroid伺服器
- 個人網站搭建之伺服器環境搭建網站伺服器
- 使用python搭建伺服器並實現Android端與之通訊Python伺服器Android
- 利用nginx搭建RTMP視訊點播、直播、HLS伺服器Nginx伺服器
- 利用雲伺服器搭建自己的微信聊天機器人伺服器機器人
- 搭建 nodeJS 伺服器之(2)sequelizeNodeJS伺服器
- 在 Android 裝置上搭建 Web 伺服器AndroidWeb伺服器
- 利用docker與shell指令碼進行懶人伺服器搭建Docker指令碼伺服器
- Android木馬利用Tor隱藏指令伺服器位置Android伺服器
- k8s之DNS伺服器搭建K8SDNS伺服器
- git伺服器gitlab之搭建和使用伺服器Gitlab
- Android 之 MIT積木搭建系統AndroidMIT
- iOS開發之mac上搭建openfire伺服器iOSMac伺服器
- 利用rman快速搭建standby
- Nginx 教程二:利用nginx搭建靜態檔案服務、正向代理伺服器、反向代理伺服器Nginx伺服器
- netty系列之:使用netty搭建websocket伺服器NettyWeb伺服器
- Android應用開發之(利用好圖片快取)Android快取
- 伺服器搭建十——MySQL資料庫伺服器搭建伺服器MySql資料庫
- 利用nodejs搭建 https 代理伺服器並實現中間人攻擊NodeJSHTTP伺服器
- netty系列之:搭建HTTP上傳檔案伺服器NettyHTTP伺服器
- BT伺服器搭建伺服器
- Git 伺服器搭建Git伺服器
- 搭建SVN伺服器伺服器
- 搭建Git伺服器Git伺服器
- NoSQL之Redis叢集搭建——6臺伺服器,三臺主伺服器,三臺從伺服器SQLRedis伺服器
- 利用Github搭建免費圖床Github圖床
- 利用docker快速搭建hexo部落格DockerHexo
- 利用github hexo搭建部落格GithubHexo
- 利用docker搭建spark hadoop workbenchDockerSparkHadoop
- netty系列之:搭建自己的下載檔案伺服器Netty伺服器
- Cobalt Strike 之團隊伺服器的搭建與DNS通訊演示伺服器DNS
- 利用 Docker 一鍵搭建 LNMP 環境DockerLNMP
- 利用Google雲搭建Flarum輕論壇Go
- 利用Selenium搭建Nunit測試框架框架
- 利用GITHUBpage搭建個人部落格Github
- 利用 GitHub 搭建獨立 Web 網站GithubWeb網站