日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

NanoHttpd源码分析

發布時間:2025/3/21 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NanoHttpd源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在GitHub上發現一個有趣的項目——NanoHttpd。

說它有趣,是因為他是一個只有一個Java文件構建而成,實現了部分http協議的http server。

GitHub地址:https://github.com/NanoHttpd/nanohttpd?

作者最近還有提交,看了下最新的代碼,寫篇源碼分析貼上來,歡迎大家多給些建議。

------------------------------------------

NanoHttpd源碼分析

NanoHttpd僅由一個文件構建而成,按照作者的意思是可以用作一個嵌入式http server。

由于它使用的是Socket BIO(阻塞IO),一個客戶端連接分發到一個線程的經典模型,而且具有良好的擴展性。所以可以算是一個學習Socket ?BIO Server比較不錯的案例,同時如果你需要編寫一個Socket Server并且不需要使用到NIO技術,那么NanoHttpd中不少代碼都可以參考復用。

NanoHTTPD.java中,啟動服務器執行start()方法,停止服務器執行stop()方法。

主要邏輯都在start()方法中:

Java代碼??

  • private?ServerSocket?myServerSocket;??
  • private?Thread?myThread;??
  • private?AsyncRunner?asyncRunner;??
  • //...??
  • 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();??
  • ????????????????????????InputStream?inputStream?=?finalAccept.getInputStream();??
  • ????????????????????????OutputStream?outputStream?=?finalAccept.getOutputStream();??
  • ????????????????????????TempFileManager?tempFileManager?=?tempFileManagerFactory.create();??
  • ????????????????????????final?HTTPSession?session?=?new?HTTPSession(tempFileManager,?inputStream,?outputStream);??
  • ????????????????????????asyncRunner.exec(new?Runnable()?{??
  • ????????????????????????????@Override??
  • ????????????????????????????public?void?run()?{??
  • ????????????????????????????????session.run();??
  • ????????????????????????????????try?{??
  • ????????????????????????????????????finalAccept.close();??
  • ????????????????????????????????}?catch?(IOException?ignored)?{??
  • ????????????????????????????????????ignored.printStackTrace();??
  • ????????????????????????????????}??
  • ????????????????????????????}??
  • ????????????????????????});??
  • ????????????????????}?catch?(IOException?e)?{??
  • ????????????????????????e.printStackTrace();??
  • ????????????????????}??
  • ????????????????}?while?(!myServerSocket.isClosed());??
  • ????????????}??
  • ????????});??
  • ????????myThread.setDaemon(true);??
  • ????????myThread.setName("NanoHttpd?Main?Listener");??
  • ????????myThread.start();??
  • }??
  • 首先,創建serversocket并綁定端口。然后開啟一個線程守護線程myThread,用作監聽客戶端連接。守護線程作用是為其它線程提供服務,就是類似于后來靜默執行的線程,當所有非守護線程執行完后,守護線程自動退出。

    當myThread線程start后,執行該線程實現runnable接口的匿名內部類run方法:

    run方法中do...while循環保證serversocket關閉前該線程一直處于監聽狀態。myServerSocket.accept()如果在沒有客戶端連接時會一直阻塞,只有客戶端連接后才會繼續執行下面的代碼。

    當客戶端連接后,獲取其input和output stream后,需要將每個客戶端連接都需要分發到一個線程中,這部分邏輯在上文中的asyncRunner.exec()內。

    Java代碼??

  • public?interface?AsyncRunner?{??
  • ?????void?exec(Runnable?code);??
  • }??
  • public?static?class?DefaultAsyncRunner?implements?AsyncRunner?{??
  • ?????private?long?requestCount;??
  • ?????@Override??
  • ?????public?void?exec(Runnable?code)?{??
  • ?????????++requestCount;??
  • ?????????Thread?t?=?new?Thread(code);??
  • ?????????t.setDaemon(true);??
  • ?????????t.setName("NanoHttpd?Request?Processor?(#"?+?requestCount?+?")");??
  • ?????????System.out.println("NanoHttpd?Request?Processor?(#"?+?requestCount?+?")");??
  • ?????????t.start();??
  • ?????}??
  • ?}??
  • DefaultAsyncRunner是NanoHTTPD的靜態內部類,實現AsyncRunner接口,作用是對每個請求創建一個線程t。每個t線程start后,會執行asyncRunner.exec()中匿名內部類的run方法:

    Java代碼??

  • TempFileManager?tempFileManager?=?tempFileManagerFactory.create();??
  • final?HTTPSession?session?=?new?HTTPSession(tempFileManager,?inputStream,?outputStream);??
  • asyncRunner.exec(new?Runnable()?{??
  • ??????????@Override??
  • ???????????public?void?run()?{??
  • ?????????????????session.run();??
  • ?????????????????try?{??
  • ????????????????????????finalAccept.close();??
  • ?????????????????}?catch?(IOException?ignored)?{??
  • ????????????????????????ignored.printStackTrace();??
  • ?????????????????}??
  • ???????????}??
  • });??
  • 該線程執行時,直接調用HTTPSession的run,執行完后關閉client連接。HTTPSession同樣是NanoHTTPD的內部類,雖然實現了Runnable接口,但是并沒有啟動線程的代碼,而是run方法直接被調用。下面主要看一下HTTPSession類中的run方法,有點長,分段解析:

    Java代碼??

  • public?static?final?int?BUFSIZE?=?8192;??
  • public?void?run()?{??
  • ????????????try?{??
  • ????????????????if?(inputStream?==?null)?{??
  • ????????????????????return;??
  • ????????????????}??
  • ????????????????byte[]?buf?=?new?byte[BUFSIZE];??
  • ????????????????int?splitbyte?=?0;??
  • ????????????????int?rlen?=?0;??
  • ????????????????{??
  • ????????????????????int?read?=?inputStream.read(buf,?0,?BUFSIZE);??
  • ????????????????????while?(read?>?0)?{??
  • ????????????????????????rlen?+=?read;??
  • ????????????????????????splitbyte?=?findHeaderEnd(buf,?rlen);??
  • ????????????????????????if?(splitbyte?>?0)??
  • ????????????????????????????break;??
  • ????????????????????????read?=?inputStream.read(buf,?rlen,?BUFSIZE?-?rlen);??
  • ????????????????????}??
  • ????????????????}??
  • ????????????????//...??
  • }??
  • 首先從inputstream中讀取8k個字節(apache默認最大header為8k),通過findHeaderEnd找到http header和body是位于哪個字節分割的--splitbyte。由于不會一次從stream中讀出8k個字節,所以找到splitbyte就直接跳出。如果沒找到,就從上次循環讀取的字節處繼續讀取一部分字節。下面看一下findHeaderEnd是怎么劃分http header和body的:

    Java代碼??

  • private?int?findHeaderEnd(final?byte[]?buf,?int?rlen)?{??
  • ????????????int?splitbyte?=?0;??
  • ????????????while?(splitbyte?+?3?<?rlen)?{??
  • ????????????????if?(buf[splitbyte]?==?'\r'?&&?buf[splitbyte?+?1]?==?'\n'?&&?buf[splitbyte?+?2]?==?'\r'?&&?buf[splitbyte?+?3]?==?'\n')?{??
  • ????????????????????return?splitbyte?+?4;??
  • ????????????????}??
  • ????????????????splitbyte++;??
  • ????????????}??
  • ????????????return?0;??
  • }??
  • 其實很簡單,http header的結束一定是兩個連續的空行(\r\n)。

    回到HTTPSession類的run方法中,讀取到splitbyte后,解析http header:

    Java代碼??

  • BufferedReader?hin?=?new?BufferedReader(new?InputStreamReader(new?ByteArrayInputStream(buf,?0,?rlen)));??
  • Map<String,?String>?pre?=?new?HashMap<String,?String>();??
  • Map<String,?String>?parms?=?new?HashMap<String,?String>();??
  • Map<String,?String>?header?=?new?HashMap<String,?String>();??
  • Map<String,?String>?files?=?new?HashMap<String,?String>();??
  • decodeHeader(hin,?pre,?parms,?header);??
  • 主要看decodeHeader方法,也比較長,簡單說一下:

    Java代碼??

  • String?inLine?=?in.readLine();??
  • if?(inLine?==?null)?{??
  • ????return;??
  • }??
  • StringTokenizer?st?=?new?StringTokenizer(inLine);??
  • if?(!st.hasMoreTokens())?{??
  • ????Response.error(outputStream,?Response.Status.BAD_REQUEST,?"BAD?REQUEST:?Syntax?error.?Usage:?GET?/example/file.html");??
  • ????throw?new?InterruptedException();??
  • }??
  • pre.put("method",?st.nextToken());??
  • if?(!st.hasMoreTokens())?{??
  • ????Response.error(outputStream,?Response.Status.BAD_REQUEST,?"BAD?REQUEST:?Missing?URI.?Usage:?GET?/example/file.html");??
  • ????throw?new?InterruptedException();??
  • }??
  • String?uri?=?st.nextToken();??
  • //?Decode?parameters?from?the?URI??
  • int?qmi?=?uri.indexOf('?');//分割參數??
  • if?(qmi?>=?0)?{??
  • ????decodeParms(uri.substring(qmi?+?1),?parms);??
  • ????uri?=?decodePercent(uri.substring(0,?qmi));??
  • }?else?{??
  • ????uri?=?decodePercent(uri);??
  • }??
  • if?(st.hasMoreTokens())?{??
  • ????String?line?=?in.readLine();??
  • ????while?(line?!=?null?&&?line.trim().length()?>?0)?{??
  • ????????int?p?=?line.indexOf(':');??
  • ????????if?(p?>=?0)??
  • ????????????header.put(line.substring(0,?p).trim().toLowerCase(),?line.substring(p?+?1).trim());??
  • ????????line?=?in.readLine();??
  • ????}??
  • }??
  • 讀取第一行,按空格分隔,解析出method和uri。最后循環解析出header內各屬性(以:分隔)。

    從decodeHeader中解析出header后,

    Java代碼??

  • Method?method?=?Method.lookup(pre.get("method"));??
  • if?(method?==?null)?{??
  • ???????????Response.error(outputStream,?Response.Status.BAD_REQUEST,?"BAD?REQUEST:?Syntax?error.");??
  • ???????????throw?new?InterruptedException();??
  • }??
  • String?uri?=?pre.get("uri");??
  • long?size?=?extractContentLength(header);//獲取content-length??
  • 獲取content-length的值,代碼就不貼了,就是從header中取出content-length屬性。

    處理完header,然后開始處理body,首先創建一個臨時文件:

    Java代碼??

  • RandomAccessFile?f?=?getTmpBucket();??
  • NanoHTTPD中將創建臨時文件進行了封裝(稍微有點復雜),如下:

    Java代碼??

  • private?final?TempFileManager?tempFileManager;??
  • private?RandomAccessFile?getTmpBucket()?{??
  • ????????????try?{??
  • ????????????????TempFile?tempFile?=?tempFileManager.createTempFile();??
  • ????????????????return?new?RandomAccessFile(tempFile.getName(),?"rw");??
  • ????????????}?catch?(Exception?e)?{??
  • ????????????????System.err.println("Error:?"?+?e.getMessage());??
  • ????????????}??
  • ????????????return?null;??
  • }??
  • 其中tempFileManager是在上文start方法中初始化傳入httpsession構造方法:

    Java代碼??

  • TempFileManager?tempFileManager?=?tempFileManagerFactory.create();??
  • final?HTTPSession?session?=?new?HTTPSession(tempFileManager,?inputStream,?outputStream);??
  • 實際的臨時文件類定義如下:

    Java代碼??

  • public?interface?TempFile?{??
  • ????????OutputStream?open()?throws?Exception;??
  • ????????void?delete()?throws?Exception;??
  • ????????String?getName();??
  • }??
  • public?static?class?DefaultTempFile?implements?TempFile?{??
  • ????????private?File?file;??
  • ????????private?OutputStream?fstream;??
  • ????????public?DefaultTempFile(String?tempdir)?throws?IOException?{??
  • ????????????file?=?File.createTempFile("NanoHTTPD-",?"",?new?File(tempdir));??
  • ????????????fstream?=?new?FileOutputStream(file);??
  • ????????}??
  • ????????@Override??
  • ????????public?OutputStream?open()?throws?Exception?{??
  • ????????????return?fstream;??
  • ????????}??
  • ????????@Override??
  • ????????public?void?delete()?throws?Exception?{??
  • ????????????file.delete();??
  • ????????}??
  • ????????@Override??
  • ????????public?String?getName()?{??
  • ????????????return?file.getAbsolutePath();??
  • ????????}??
  • }??
  • public?static?class?DefaultTempFileManager?implements?TempFileManager?{??
  • ????????private?final?String?tmpdir;??
  • ????????private?final?List<TempFile>?tempFiles;??
  • ????????public?DefaultTempFileManager()?{??
  • ????????????tmpdir?=?System.getProperty("java.io.tmpdir");??
  • ????????????tempFiles?=?new?ArrayList<TempFile>();??
  • ????????}??
  • ????????@Override??
  • ????????public?TempFile?createTempFile()?throws?Exception?{??
  • ????????????DefaultTempFile?tempFile?=?new?DefaultTempFile(tmpdir);??
  • ????????????tempFiles.add(tempFile);??
  • ????????????return?tempFile;??
  • ????????}??
  • ????????@Override??
  • ????????public?void?clear()?{??
  • ????????????for?(TempFile?file?:?tempFiles)?{??
  • ????????????????try?{??
  • ????????????????????file.delete();??
  • ????????????????}?catch?(Exception?ignored)?{??
  • ????????????????}??
  • ?????????}??
  • ?????????tempFiles.clear();??
  • }??
  • 可以看到,臨時文件的創建使用的是File.createTempFile方法,臨時文件存放目錄在java.io.tmpdir所定義的系統屬性下,臨時文件的類型是RandomAccessFile,該類支持對文件任意位置的讀取和寫入。

    繼續回到HttpSession的run方法內,從上文中解析出的splitbyte處將body讀出并寫入剛才創建的臨時文件:

    Java代碼??

  • if?(splitbyte?<?rlen)?{??
  • ????f.write(buf,?splitbyte,?rlen?-?splitbyte);??
  • }??
  • ??
  • if?(splitbyte?<?rlen)?{??
  • ????size?-=?rlen?-?splitbyte?+?1;???
  • }?else?if?(splitbyte?==?0?||?size?==?0x7FFFFFFFFFFFFFFFl)?{??
  • ????size?=?0;??
  • }??
  • ??
  • //?Now?read?all?the?body?and?write?it?to?f??
  • buf?=?new?byte[512];??
  • while?(rlen?>=?0?&&?size?>?0)?{????
  • ????rlen?=?inputStream.read(buf,?0,?512);??
  • ????size?-=?rlen;??
  • ????if?(rlen?>?0)?{??
  • ????????f.write(buf,?0,?rlen);??
  • ????}??
  • }??
  • System.out.println("buf?body:"+new?String(buf));??
  • 然后,創建一個bufferedreader以方便讀取該文件。注意,此處對文件的訪問使用的是NIO內存映射,seek(0)表示將文件指針指向文件頭。

    Java代碼??

  • //?Get?the?raw?body?as?a?byte?[]??
  • ByteBuffer?fbuf?=?f.getChannel().map(FileChannel.MapMode.READ_ONLY,?0,?f.length());??
  • f.seek(0);??
  • //?Create?a?BufferedReader?for?easily?reading?it?as?string.??
  • InputStream?bin?=?new?FileInputStream(f.getFD());??
  • BufferedReader?in?=?new?BufferedReader(new?InputStreamReader(bin));??
  • 之后,如果請求是POST方法,則取出content-type,并對multipart/form-data(上傳)和application/x-www-form-urlencoded(表單提交)分別進行了處理:

    Java代碼??

  • if?(Method.POST.equals(method))?{??
  • ????????????????????String?contentType?=?"";??
  • ????????????????????String?contentTypeHeader?=?header.get("content-type");??
  • ????????????????????StringTokenizer?st?=?null;??
  • ????????????????????if?(contentTypeHeader?!=?null)?{??
  • ????????????????????????st?=?new?StringTokenizer(contentTypeHeader,?",;?");??
  • ????????????????????????if?(st.hasMoreTokens())?{??
  • ????????????????????????????contentType?=?st.nextToken();??
  • ????????????????????????}??
  • ????????????????????}??
  • ????????????????????if?("multipart/form-data".equalsIgnoreCase(contentType))?{??
  • ????????????????????????//?Handle?multipart/form-data??
  • ????????????????????????if?(!st.hasMoreTokens())?{??
  • ????????????????????????????Response.error(outputStream,?Response.Status.BAD_REQUEST,?"BAD?REQUEST:?Content?type?is?multipart/form-data?but?boundary?missing.?Usage:?GET?/example/file.html");??
  • ????????????????????????????throw?new?InterruptedException();??
  • ????????????????????????}??
  • ????????????????????????String?boundaryStartString?=?"boundary=";??
  • ????????????????????????int?boundaryContentStart?=?contentTypeHeader.indexOf(boundaryStartString)?+?boundaryStartString.length();??
  • ????????????????????????String?boundary?=?contentTypeHeader.substring(boundaryContentStart,?contentTypeHeader.length());??
  • ????????????????????????if?(boundary.startsWith("\"")?&&?boundary.startsWith("\""))?{??
  • ????????????????????????????boundary?=?boundary.substring(1,?boundary.length()?-?1);??
  • ????????????????????????}??
  • ????????????????????????decodeMultipartData(boundary,?fbuf,?in,?parms,?files);//??
  • ????????????????????}?else?{??
  • ????????????????????????//?Handle?application/x-www-form-urlencoded??
  • ????????????????????????String?postLine?=?"";??
  • ????????????????????????char?pbuf[]?=?new?char[512];??
  • ????????????????????????int?read?=?in.read(pbuf);??
  • ????????????????????????while?(read?>=?0?&&?!postLine.endsWith("\r\n"))?{??
  • ????????????????????????????postLine?+=?String.valueOf(pbuf,?0,?read);??
  • ????????????????????????????read?=?in.read(pbuf);??
  • ????????????????????????}??
  • ????????????????????????postLine?=?postLine.trim();??
  • ????????????????????????decodeParms(postLine,?parms);//??
  • ????????????????????}??
  • }???
  • 這里需要注意的是,如果是文件上傳的請求,根據HTTP協議就不能再使用a=b的方式了,而是使用分隔符的方式。例如:Content-Type:multipart/form-data; boundary=--AaB03x中boundary=后面的這個--AaB03x就是分隔符:

    Request代碼??

  • --AaB03x??
  • Content-Disposition:?form-data;?name="submit-name"??//表單域名-submit-name??
  • shensy??//表單域值??
  • --AaB03x??
  • Content-Disposition:?form-data;?name="file";?filename="a.exe"?//上傳文件??
  • Content-Type:?application/octet-stream??
  • a.exe文件的二進制數據??
  • --AaB03x--??//結束分隔符??
  • 如果是普通的表單提交的話,就循環讀取post body直到結束(\r\n)為止。

    另外,簡單看了一下:decodeMultipartData作用是將post中上傳文件的內容解析出來,decodeParms作用是將post中含有%的值使用URLDecoder.decode解碼出來,這里就不貼代碼了。

    最后,除了處理POST請求外,還對PUT請求進行了處理。

    Java代碼??

  • else?if?(Method.PUT.equals(method))?{??
  • ?????????files.put("content",?saveTmpFile(fbuf,?0,?fbuf.limit()));??
  • }??
  • 其中,saveTmpFile方法是將body寫入臨時文件并返回其路徑,limit為當前buffer中可用的位置(即內容):

    Java代碼??

  • private?String?saveTmpFile(ByteBuffer??b,?int?offset,?int?len)?{??
  • ????????????String?path?=?"";??
  • ????????????if?(len?>?0)?{??
  • ????????????????try?{??
  • ????????????????????TempFile?tempFile?=?tempFileManager.createTempFile();??
  • ????????????????????ByteBuffer?src?=?b.duplicate();??
  • ????????????????????FileChannel?dest?=?new?FileOutputStream(tempFile.getName()).getChannel();??
  • ????????????????????src.position(offset).limit(offset?+?len);??
  • ????????????????????dest.write(src.slice());??
  • ????????????????????path?=?tempFile.getName();??
  • ????????????????}?catch?(Exception?e)?{?//?Catch?exception?if?any??
  • ????????????????????System.err.println("Error:?"?+?e.getMessage());??
  • ????????????????}??
  • ????????????}??
  • ????????????return?path;??
  • }??
  • 現在,所有請求處理完成,下面構造響應并關閉流:

    Java代碼??

  • Response?r?=?serve(uri,?method,?header,?parms,?files);??
  • if?(r?==?null)?{??
  • ????Response.error(outputStream,?Response.Status.INTERNAL_ERROR,?"SERVER?INTERNAL?ERROR:?Serve()?returned?a?null?response.");??
  • ????throw?new?InterruptedException();??
  • }?else?{??
  • ????r.setRequestMethod(method);??
  • ????r.send(outputStream);??
  • }??
  • in.close();??
  • inputStream.close();??
  • 其中serve是個抽象方法,用于構造響應內容,需要用戶在子類中實現(后面會給出例子)。

    Java代碼??

  • public?abstract?Response?serve(String?uri,Method?method,Map<String,?String>?header,Map<String,?String>?parms,Map<String,?String>?files);??
  • 構造完響應內容,最后就是發送響應了:

    Java代碼??

  • private?void?send(OutputStream?outputStream)?{??
  • ????????????String?mime?=?mimeType;??
  • ????????????SimpleDateFormat?gmtFrmt?=?new?SimpleDateFormat("E,?d?MMM?yyyy?HH:mm:ss?'GMT'",?Locale.US);??
  • ????????????gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));??
  • ????????????try?{??
  • ????????????????if?(status?==?null)?{??
  • ????????????????????throw?new?Error("sendResponse():?Status?can't?be?null.");??
  • ????????????????}??
  • ????????????????PrintWriter?pw?=?new?PrintWriter(outputStream);??
  • ????????????????pw.print("HTTP/1.0?"?+?status.getDescription()?+?"?\r\n");??
  • ????????????????if?(mime?!=?null)?{??
  • ????????????????????pw.print("Content-Type:?"?+?mime?+?"\r\n");??
  • ????????????????}??
  • ????????????????if?(header?==?null?||?header.get("Date")?==?null)?{??
  • ????????????????????pw.print("Date:?"?+?gmtFrmt.format(new?Date())?+?"\r\n");??
  • ????????????????}??
  • ????????????????if?(header?!=?null)?{??
  • ????????????????????for?(String?key?:?header.keySet())?{??
  • ????????????????????????String?value?=?header.get(key);??
  • ????????????????????????pw.print(key?+?":?"?+?value?+?"\r\n");??
  • ????????????????????}??
  • ????????????????}??
  • ????????????????pw.print("\r\n");??
  • ????????????????pw.flush();??
  • ????????????????if?(requestMethod?!=?Method.HEAD?&&?data?!=?null)?{??
  • ????????????????????int?pending?=?data.available();??
  • ????????????????????int?BUFFER_SIZE?=?16?*?1024;??
  • ????????????????????byte[]?buff?=?new?byte[BUFFER_SIZE];??
  • ????????????????????while?(pending?>?0)?{??
  • ????????????????????????int?read?=?data.read(buff,?0,?((pending?>?BUFFER_SIZE)???BUFFER_SIZE?:?pending));??
  • ????????????????????????if?(read?<=?0)?{??
  • ????????????????????????????break;??
  • ????????????????????????}??
  • ????????????????????????outputStream.write(buff,?0,?read);??
  • ????????????????????????pending?-=?read;??
  • ????????????????????}??
  • ????????????????}??
  • ????????????????outputStream.flush();??
  • ????????????????outputStream.close();??
  • ????????????????if?(data?!=?null)??
  • ????????????????????data.close();??
  • ????????????}?catch?(IOException?ioe)?{??
  • ????????????????//?Couldn't?write??No?can?do.??
  • ????????????}??
  • }??
  • 通過PrintWriter構造響應頭,如果請求不為HEAD方法(沒有響應body),則將用戶構造的響應內容寫入outputStream作為響應體。

    下面給出一個使用案例(官方提供):

    Java代碼??

  • public?class?HelloServer?extends?NanoHTTPD?{??
  • ????public?HelloServer()?{??
  • ????????super(8080);??
  • ????}??
  • ????@Override??
  • ????public?Response?serve(String?uri,?Method?method,?Map<String,?String>?header,?Map<String,?String>?parms,?Map<String,?String>?files)?{??
  • ????????String?msg?=?"<html><body><h1>Hello?server</h1>\n";??
  • ????????if?(parms.get("username")?==?null)??
  • ????????????msg?+=??
  • ????????????????????"<form?action='?'?method='post'>\n"?+??
  • ????????????????????????????"??<p>Your?name:?<input?type='text'?name='username'></p>\n"?+??
  • ????????????????????????????"</form>\n";??
  • ????????else??
  • ????????????msg?+=?"<p>Hello,?"?+?parms.get("username")?+?"!</p>";??
  • ????????msg?+=?"</body></html>\n";??
  • ????????return?new?NanoHTTPD.Response(msg);??
  • ????}??
  • ????//后面public?static?void?main...就不貼了??
  • }??
  • 由此可見,serve是上文中的抽象方法,由用戶構造響應內容,此處給出的例子是一個html。

    結束語:

    至此,NanoHTTPD的源碼基本就算分析完了。通過分析該源碼,可以更深入的了解Socket BIO編程模型以及HTTP協議請求響應格式。希望能對看到的人有所幫助,同時歡迎大家多拍磚。

    總結

    以上是生活随笔為你收集整理的NanoHttpd源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。