新聞中心
HTTP是個大協(xié)議,完整功能的HTTP服務器必須響應資源請求,將URL轉換為本地系統(tǒng)的資源名。響應各種形式的HTTP請求(GET、POST等)。處理不存在的文件請求,返回各種形式的狀態(tài)碼,解析MIME類型等。但許多特定功能的HTTP服務器并不需要所有這些功能。例如,很多網(wǎng)站只是想顯示“建設中“的消息。很顯然,Apache對于這樣的網(wǎng)站是大材小用了。這樣的網(wǎng)站完全可以使用只做一件事情的定制服務器。Java網(wǎng)絡類庫使得編寫這樣的單任務服務器輕而易舉。

泉港網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁設計、網(wǎng)站建設、微信開發(fā)、APP開發(fā)、成都響應式網(wǎng)站建設公司等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)公司成立于2013年到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設就選創(chuàng)新互聯(lián)公司。
定制服務器不只是用于小網(wǎng)站。大流量的網(wǎng)站如Yahoo,也使用定制服務器,因為與一般用途的服務器相比,只做一件事情的服務器通常要快得多。針對某項任務來優(yōu)化特殊用途的服務器很容易;其結果往往比需要響應很多種請求的一般用途服務器高效得多。例如,對于重復用于多頁面或大流量頁面中的圖標和圖片,用一個單獨的服務器處理會更好(并且還可以避免在請求時攜帶不必要的Cookie,因而可以減少請求/響應數(shù)據(jù),從而減少下載帶寬,提升速度);這個服務器在啟動時把所有圖片文件讀入內(nèi)存,從RAM中直接提供這些文件,而不是每次請求都從磁盤上讀取。此外,如果你不想在包含這些圖片的頁面請求之外單獨記錄這些圖片,這個單獨服務器則會避免在日志記錄上浪費時間。
本篇為大家簡要演示三種HTTP服務器:
(1) 簡單的單文件服務器
(2) 重定向服務器
(3) 完整功能的HTTP服務器
簡單的單文件服務器
該服務器的功能:無論接受到何種請求,都始終發(fā)送同一個文件。這個服務器命名為SingleFileHTTPServer,文件名、本地端口和內(nèi)容編碼方式從命令行讀取。如果缺省端口,則假定端口號為80。如果缺省編碼方式,則假定為ASCII。
- import java.io.*;
- import java.net.ServerSocket;
- import java.net.Socket;
- public class SingleFileHTTPServer extends Thread {
- private byte[] content;
- private byte[] header;
- private int port=80;
- private SingleFileHTTPServer(String data, String encoding,
- String MIMEType, int port) throws UnsupportedEncodingException {
- this(data.getBytes(encoding), encoding, MIMEType, port);
- }
- public SingleFileHTTPServer(byte[] data, String encoding, String MIMEType, int port)throws UnsupportedEncodingException {
- this.content=data;
- this.port=port;
- String header="HTTP/1.0 200 OK\r\n"+
- "Server: OneFile 1.0\r\n"+
- "Content-length: "+this.content.length+"\r\n"+
- "Content-type: "+MIMEType+"\r\n\r\n";
- this.header=header.getBytes("ASCII");
- }
- public void run() {
- try {
- ServerSocket server=new ServerSocket(this.port);
- System.out.println("Accepting connections on port "+server.getLocalPort());
- System.out.println("Data to be sent:");
- System.out.write(this.content);
- while (true) {
- Socket connection=null;
- try {
- connection=server.accept();
- OutputStream out=new BufferedOutputStream(connection.getOutputStream());
- InputStream in=new BufferedInputStream(connection.getInputStream());
- StringBuffer request=new StringBuffer();
- while (true) {
- int c=in.read();
- if (c=='\r'||c=='\n'||c==-1) {
- break;
- }
- request.append((char)c);
- }
- //如果檢測到是HTTP/1.0及以后的協(xié)議,按照規(guī)范,需要發(fā)送一個MIME首部
- if (request.toString().indexOf("HTTP/")!=-1) {
- out.write(this.header);
- }
- out.write(this.content);
- out.flush();
- } catch (IOException e) {
- // TODO: handle exception
- }finally{
- if (connection!=null) {
- connection.close();
- }
- }
- }
- } catch (IOException e) {
- System.err.println("Could not start server. Port Occupied");
- }
- }
- public static void main(String[] args) {
- try {
- String contentType="text/plain";
- if (args[0].endsWith(".html")||args[0].endsWith(".htm")) {
- contentType="text/html";
- }
- InputStream in=new FileInputStream(args[0]);
- ByteArrayOutputStream out=new ByteArrayOutputStream();
- int b;
- while ((b=in.read())!=-1) {
- out.write(b);
- }
- byte[] data=out.toByteArray();
- //設置監(jiān)聽端口
- int port;
- try {
- port=Integer.parseInt(args[1]);
- if (port<1||port>65535) {
- port=80;
- }
- } catch (Exception e) {
- port=80;
- }
- String encoding="ASCII";
- if (args.length>2) {
- encoding=args[2];
- }
- Thread t=new SingleFileHTTPServer(data, encoding, contentType, port);
- t.start();
- } catch (ArrayIndexOutOfBoundsException e) {
- System.out.println("Usage:java SingleFileHTTPServer filename port encoding");
- }catch (Exception e) {
- System.err.println(e);// TODO: handle exception
- }
- }
- }
SingleFileHTTPServer類本身是Thread的子類。它的run()方法處理入站連接。此服務器可能只是提供小文件,而且只支持低吞吐量的web網(wǎng)站。由于服務器對每個連接所需完成的所有工作就是檢查客戶端是否支持HTTP/1.0,并為連接生成一兩個較小的字節(jié)數(shù)組,因此這可能已經(jīng)足夠了。另一方面,如果你發(fā)現(xiàn)客戶端被拒絕,則可以使用多線程。許多事情取決于所提供文件的大小,每分鐘所期望連接的峰值數(shù)和主機上Java的線程模型。對弈這個程序復雜寫的服務器,使用多線程將會有明顯的收益。
Run()方法在指定端口創(chuàng)建一個ServerSocket。然后它進入無限循環(huán),不斷地接受連接并處理連接。當接受一個socket時,就會由一個InputStream從客戶端讀取請求。它查看***行是否包含字符串HTTP。如果包含此字符串,服務器就假定客戶端理解HTTP/1.0或以后的版本,因此為該文件發(fā)送一個MIME首部;然后發(fā)送數(shù)據(jù)。如果客戶端請求不包含字符串HTTP,服務器就忽略首部,直接發(fā)送數(shù)據(jù)。***服務器關閉連接,嘗試接受下一個連接。
而main()方法只是從命令行讀取參數(shù)。從***個命令行參數(shù)讀取要提供的文件名。如果沒有指定文件或者文件無法打開,就顯示一條錯誤信息,程序退出。如果文件能夠讀取,其內(nèi)容就讀入byte數(shù)組data.關于文件的內(nèi)容類型,將進行合理的猜測,結果存儲在contentType變量中。接下來,從第二個命令行參數(shù)讀取端口號。如果沒有指定端口或第二個參數(shù)不是0到65535之間的整數(shù),就使用端口80。從第三個命令行參數(shù)讀取編碼方式(前提是提供了)。否則,編碼方式就假定為ASCII。然后使用這些值構造一個SingleFileHTTPServer對象,開始運行。這是唯一可能的接口。
下面是測試的結果:
命令行編譯代碼并設置參數(shù):
telnet:
首先,啟用telnet服務(如果不會,自行google之),接著測試該主機的端口:
結果(可以看到請求的輸出內(nèi)容):
HTTP協(xié)議測試:
文檔(這是之前一篇文章--小車動畫的文檔):
重定向服務器
實現(xiàn)的功能——將用戶從一個Web網(wǎng)站重定向到另一個站點。下例從命令行讀取URL和端口號,打開此端口號的服務器可能速度會很快,因此不需要多線程。盡管日次,使用多線程可能還是會帶來一些好處,尤其是對于網(wǎng)絡帶寬很低、吞吐量很小的網(wǎng)站。在此主要是為了演示,所以,已經(jīng)將該服務器做成多線程的了。這里為了簡單起見,為每個連接都啟用了一個線程,而不是采用線程池。或許更便于理解,但這真的有些浪費系統(tǒng)資源并且顯得低效。
- import java.io.BufferedInputStream;
- import java.io.BufferedWriter;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.OutputStreamWriter;
- import java.io.Reader;
- import java.io.Writer;
- import java.net.BindException;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Date;
- public class Redirector implements Runnable {
- private int port;
- private String newSite;
- public Redirector(String site, int port){
- this.port=port;
- this.newSite=site;
- }
- @Override
- public void run() {
- try {
- ServerSocket server=new ServerSocket(port);
- System.out.println("Redirecting connection on port"
- +server.getLocalPort()+" to "+newSite);
- while (true) {
- try {
- Socket socket=server.accept();
- Thread thread=new RedirectThread(socket);
- thread.start();
- } catch (IOException e) {
- // TODO: handle exception
- }
- }
- } catch (BindException e) {
- System.err.println("Could not start server. Port Occupied");
- }catch (IOException e) {
- System.err.println(e);
- }
- }
- class RedirectThread extends Thread {
- private Socket connection;
- RedirectThread(Socket s) {
- this.connection=s;
- }
- public void run() {
- try {
- Writer out=new BufferedWriter(
- new OutputStreamWriter(connection.getOutputStream(),"ASCII"));
- Reader in=new InputStreamReader(
- new BufferedInputStream(connection.getInputStream()));
- StringBuffer request=new StringBuffer(80);
- while (true) {
- int c=in.read();
- if (c=='\t'||c=='\n'||c==-1) {
- break;
- }
- request.append((char)c);
- }
- String get=request.toString();
- int firstSpace=get.indexOf(' ');
- int secondSpace=get.indexOf(' ', firstSpace+1);
- String theFile=get.substring(firstSpace+1, secondSpace);
- if (get.indexOf("HTTP")!=-1) {
- out.write("HTTP/1.0 302 FOUND\r\n");
- Date now=new Date();
- out.write("Date: "+now+"\r\n");
- out.write("Server: Redirector 1.0\r\n");
- out.write("Location: "+newSite+theFile+"\r\n");
- out.write("Content-Type: text/html\r\n\r\n");
- out.flush();
- }
- //并非所有的瀏覽器都支持重定向,
- //所以我們需要生成一個適用于所有瀏覽器的HTML文件,來描述這一行為
- out.write("
Document moved \r\n");- out.write("
Document moved
\r\n");- out.write("The document "+theFile
- +" has moved to \r\n"
- +newSite+theFile
- +".\r\n Please update your bookmarks");
- out.write("


咨詢
建站咨詢