JAVA實現HTTP伺服器端

來源:互聯網
上載者:User

用java socket實現了一個簡單的http伺服器, 可以處理GET, POST,以及帶一個附件的multipart類型的POST。雖然中途遇到了很多問題, 不過通過在論壇和幾個高手交流了一下,問題都解決了。如果你覺得程式有些地方看不明白,可以參看這個文章:http://topic.csdn.net/u/20090625/22/59a5bfc8-a6b6-445d-9829-ea6d462a4fe6.html .

雖然解析http頭不是很規範,本來應該用原始的位元組流, 我採用了一個折衷的方案,用DataInputStream.

本代碼的實用性==0,但是可以協助很好地瞭解http協議,然後其他的應用程式層協議大都如此。

如果你從來都沒有瞭解過http協議,建議先搜尋閱讀一下,或者你還可以用下面的代碼來簡單的看一看到底瀏覽器和伺服器之間都相互發送了什麼資料。

MyHttpClient.java: 類比瀏覽器的行為, 向伺服器發送get/post請求,然後列印出伺服器返回的訊息。這樣就可以查看當一個請求到來之後, 伺服器到底都給瀏覽器發送了哪些訊息。

package socket;</p><p>import java.io.*;<br />import java.net.*;</p><p>public class MyHttpClient {<br />public static void main(String[] args) throws Exception{<br />InetAddress inet = InetAddress.getByName("www.baidu.com");<br />System.out.println(inet.getHostAddress());<br />Socket socket = new Socket(inet.getHostAddress(),80);<br />InputStream in = socket.getInputStream();<br />OutputStream out = socket.getOutputStream();<br />BufferedReader reader = new BufferedReader(new InputStreamReader(in));<br />PrintWriter writer = new PrintWriter(out);<br />writer.println("GET /home.html HTTP/1.1");//home.html是關於百度的頁面<br />writer.println("Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, */*");<br />writer.println("Accept-Language: en-us,zh-cn;q=0.5");<br />writer.println("Accept-Encoding: gzip, deflate");<br />writer.println("Host: www.baidu.com");<br />writer.println("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");<br />writer.println("Connection: Keep-Alive");<br />writer.println();<br />writer.flush();<br />String line = reader.readLine();<br />while(line!=null){<br />System.out.println(line);<br />line = reader.readLine();<br />}<br />reader.close();<br />writer.close();<br />}<br />}

MyServer.java: 類比server端接收瀏覽器的請求,然後把整個請求的報文列印出來。程式運行之後直接用瀏覽器測試。

package socket;</p><p>import java.io.*;<br />import java.net.*;</p><p>public class MyServer {<br />public static void main(String[] args) throws IOException{<br />ServerSocket svrSocket = new ServerSocket(8080);<br />while(true){<br />Socket socket = svrSocket.accept();<br />//足夠大的一個緩衝區<br />byte[] buf = new byte[1024*1024];<br />InputStream in = socket.getInputStream();<br />int byteRead = in.read(buf, 0, 1024*1024);<br />String dataString = new String(buf, 0, byteRead);<br />System.out.println(dataString);<br />in.close();<br />socket.close();<br />}<br />}<br />}<br />

主程式MyHttpServer.

package socket;</p><p>import java.io.*;<br />import java.net.*;<br />/**<br /> * MyHttpServer 實現一個簡單的HTTP伺服器端,可以擷取使用者提交的內容<br /> * 並給使用者一個response<br /> * 因為時間的關係,對http頭的處理顯得不規範<br /> * 對於上傳附件,暫時只能解析只上傳一個附件而且附件位置在第一個的情況<br /> * 轉載請註明來自http://blog.csdn.net/sunxing007<br /> * **/<br />public class MyHttpServer {<br /> //伺服器根目錄,post.html, upload.html都放在該位置<br /> public static String WEB_ROOT = "c:/root";<br /> //連接埠<br /> private int port;<br /> //使用者請求的檔案的url<br /> private String requestPath;<br /> //mltipart/form-data方式提交post的分隔字元,<br /> private String boundary = null;<br /> //post提交請求的本文的長度<br /> private int contentLength = 0;</p><p> public MyHttpServer(String root, int port) {<br /> WEB_ROOT = root;<br /> this.port = port;<br /> requestPath = null;<br /> }<br /> //處理GET請求<br /> private void doGet(DataInputStream reader, OutputStream out) throws Exception {<br /> if (new File(WEB_ROOT + this.requestPath).exists()) {<br /> //從伺服器根目錄下找到使用者請求的檔案並發送回瀏覽器<br /> InputStream fileIn = new FileInputStream(WEB_ROOT + this.requestPath);<br /> byte[] buf = new byte[fileIn.available()];<br /> fileIn.read(buf);<br /> out.write(buf);<br /> out.close();<br /> fileIn.close();<br /> reader.close();<br /> System.out.println("request complete.");<br /> }<br /> }<br /> //處理post請求<br /> private void doPost(DataInputStream reader, OutputStream out) throws Exception {<br /> String line = reader.readLine();<br /> while (line != null) {<br /> System.out.println(line);<br /> line = reader.readLine();<br /> if ("".equals(line)) {<br /> break;<br /> } else if (line.indexOf("Content-Length") != -1) {<br /> this.contentLength = Integer.parseInt(line.substring(line.indexOf("Content-Length") + 16));<br /> }<br /> //表明要上傳附件, 跳轉到doMultiPart方法。<br /> else if(line.indexOf("multipart/form-data")!= -1){<br /> //得multiltipart的分隔字元<br /> this.boundary = line.substring(line.indexOf("boundary") + 9);<br /> this.doMultiPart(reader, out);<br /> return;<br /> }<br /> }<br /> //繼續讀取普通post(沒有附件)提交的資料<br /> System.out.println("begin reading posted data......");<br /> String dataLine = null;<br /> //使用者發送的post資料本文<br /> byte[] buf = {};<br /> int size = 0;<br /> if (this.contentLength != 0) {<br /> buf = new byte[this.contentLength];<br /> while(size<this.contentLength){<br /> int c = reader.read();<br /> buf[size++] = (byte)c;</p><p> }<br /> System.out.println("The data user posted: " + new String(buf, 0, size));<br /> }<br /> //發送回瀏覽器的內容<br /> String response = "";<br /> response += "HTTP/1.1 200 OK/n";<br /> response += "Server: Sunpache 1.0/n";<br /> response += "Content-Type: text/html/n";<br /> response += "Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT/n";<br /> response += "Accept-ranges: bytes";<br /> response += "/n";<br /> String body = "<html><head><title>test server</title></head><body><p>post ok:</p>" + new String(buf, 0, size) + "</body></html>";<br /> System.out.println(body);<br /> out.write(response.getBytes());<br /> out.write(body.getBytes());<br /> out.flush();<br /> reader.close();<br /> out.close();<br /> System.out.println("request complete.");<br /> }<br /> //處理附件<br /> private void doMultiPart(DataInputStream reader, OutputStream out) throws Exception {<br /> System.out.println("doMultiPart ......");<br /> String line = reader.readLine();<br /> while (line != null) {<br /> System.out.println(line);<br /> line = reader.readLine();<br /> if ("".equals(line)) {<br /> break;<br /> } else if (line.indexOf("Content-Length") != -1) {<br /> this.contentLength = Integer.parseInt(line.substring(line.indexOf("Content-Length") + 16));<br /> System.out.println("contentLength: " + this.contentLength);<br /> } else if (line.indexOf("boundary") != -1) {<br /> //擷取multipart分隔字元<br /> this.boundary = line.substring(line.indexOf("boundary") + 9);<br /> }<br /> }<br /> System.out.println("begin get data......");<br /> /*下面的注釋是一個瀏覽器發送帶附件的請求的全文,所有中文都是說明性的文字*****<br /> <HTTP頭部內容略><br /> ............<br /> Cache-Control: no-cache<br /> <這裡有一個空行,表明接下來的內容都是要提交的本文><br /> -----------------------------7d925134501f6<這是multipart分隔字元><br /> Content-Disposition: form-data; name="myfile"; filename="mywork.doc"<br /> Content-Type: text/plain</p><p> <附件本文>........................................<br /> .................................................</p><p> -----------------------------7d925134501f6<這是multipart分隔字元><br /> Content-Disposition: form-data; name="myname"<其他欄位或附件><br /> <這裡有一個空行><br /> <其他欄位或附件的內容><br /> -----------------------------7d925134501f6--<這是multipart分隔字元,最後一個分隔字元多兩個-><br /> ****************************************************************/<br /> /**<br /> * 上面的注釋是一個帶附件的multipart類型的POST的全文模型,<br /> * 要把附件去出來,就是要找到附件本文的起始位置和結束位置<br /> * **/<br /> if (this.contentLength != 0) {<br /> //把所有的提交的本文,包括附件和其他欄位都先讀到buf.<br /> byte[] buf = new byte[this.contentLength];<br /> int totalRead = 0;<br /> int size = 0;<br /> while (totalRead < this.contentLength) {<br /> size = reader.read(buf, totalRead, this.contentLength - totalRead);<br /> totalRead += size;<br /> }<br /> //用buf構造一個字串,可以用字串方便的計算出附件所在的位置<br /> String dataString = new String(buf, 0, totalRead);<br /> System.out.println("the data user posted:/n" + dataString);<br /> int pos = dataString.indexOf(boundary);<br /> //以下略過4行就是第一個附件的位置<br /> pos = dataString.indexOf("/n", pos) + 1;<br /> pos = dataString.indexOf("/n", pos) + 1;<br /> pos = dataString.indexOf("/n", pos) + 1;<br /> pos = dataString.indexOf("/n", pos) + 1;<br /> //附件開始位置<br /> int start = dataString.substring(0, pos).getBytes().length;<br /> pos = dataString.indexOf(boundary, pos) - 4;<br /> //附件結束位置<br /> int end = dataString.substring(0, pos).getBytes().length;<br /> //以下找出filename<br /> int fileNameBegin = dataString.indexOf("filename") + 10;<br /> int fileNameEnd = dataString.indexOf("/n", fileNameBegin);<br /> String fileName = dataString.substring(fileNameBegin, fileNameEnd);<br /> /**<br /> * 有時候上傳的檔案顯示完整的檔案名稱路徑,比如c:/my file/somedir/project.doc<br /> * 但有時候只顯示檔案的名字,比如myphoto.jpg.<br /> * 所以需要做一個判斷。<br /> */<br /> if(fileName.lastIndexOf("//")!=-1){<br /> fileName = fileName.substring(fileName.lastIndexOf("//") + 1);<br /> }<br /> fileName = fileName.substring(0, fileName.length()-2);<br /> OutputStream fileOut = new FileOutputStream("c://" + fileName);<br /> fileOut.write(buf, start, end-start);<br /> fileOut.close();<br /> fileOut.close();<br /> }<br /> String response = "";<br /> response += "HTTP/1.1 200 OK/n";<br /> response += "Server: Sunpache 1.0/n";<br /> response += "Content-Type: text/html/n";<br /> response += "Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT/n";<br /> response += "Accept-ranges: bytes";<br /> response += "/n";<br /> out.write("<html><head><title>test server</title></head><body><p>Post is ok</p></body></html>".getBytes());<br /> out.flush();<br /> reader.close();<br /> System.out.println("request complete.");<br /> }</p><p> public void service() throws Exception {<br /> ServerSocket serverSocket = new ServerSocket(this.port);<br /> System.out.println("server is ok.");<br /> //開啟serverSocket等待使用者請求到來,然後根據請求的類別作處理<br /> //在這裡我只針對GET和POST作了處理<br /> //其中POST具有解析單個附件的能力<br /> while (true) {<br /> Socket socket = serverSocket.accept();<br /> System.out.println("new request coming.");<br /> DataInputStream reader = new DataInputStream((socket.getInputStream()));<br /> String line = reader.readLine();<br /> String method = line.substring(0, 4).trim();<br /> OutputStream out = socket.getOutputStream();<br /> this.requestPath = line.split(" ")[1];<br /> System.out.println(method);<br /> if ("GET".equalsIgnoreCase(method)) {<br /> System.out.println("do get......");<br /> this.doGet(reader, out);<br /> } else if ("POST".equalsIgnoreCase(method)) {<br /> System.out.println("do post......");<br /> this.doPost(reader, out);<br /> }<br /> socket.close();<br /> System.out.println("socket closed.");<br /> }<br /> }<br /> public static void main(String args[]) throws Exception {<br /> MyHttpServer server = new MyHttpServer("c:/root", 8080);<br /> server.service();<br /> }<br />}

測試檔案post.html, upload.html都放在上面程式定義的WEB_ROOT下面。

post.html:處理普通的post請求

<html><br /><head><br /><title>test my server</title><br /><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><br /></head><br /><body><br /><p>upload</p><br />來自http://blog.csdn.net/sunxing007<br><br /><form name="UploadForm" method="post" action="http://localhost:8080/"><br /><input type="text" name="myname" /><br><br /><select name="myage"><br /> <option value="18">18</option><br /> <option value="20">20</option><br /> <option value="22">22</option><br /></select><br><br /><input type="submit"value="Sutmit"><br /></form><br /></body><br /></html></p><p>

upload.html:測試帶附件的post請求

<head><br /><title>my page</title><br /><style><br /> table{<br /> border-collapse: collapse;<br /> }<br /></style><br /></head><br /><body><br />來自http://blog.csdn.net/sunxing007<br><br /> <form action='http://localhost:8080/' method='post' enctype='multipart/form-data'><br /> file: <input type='file' name='myfile' /><br><br /> <input type='submit' /><br /> </form><br /></body><br /></html>

一切準備妥當,並且MyHttpServer運行之後, 在瀏覽器輸入http://localhost:8080/post.html和http://localhost:8080/upload.html即可進行測試.

轉載請註明來自http://blog.csdn.net/sunxing007

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.