Java的網路編程:用Java實現Web伺服器

來源:互聯網
上載者:User
web|web服務|web伺服器|編程|網路 超文字傳輸通訊協定 (HTTP)(HTTP)是位於TCP/IP 協議的應用程式層,是最廣為人知的協議,也是互連網中最核心的協議之一,同樣,HTTP 也是基於 C/S 或 B/S 模型實現的。事實上,我們使用的瀏覽器如Netscape 或IE 是實現HTTP 協議中的用戶端,而一些常用的Web 服務器軟體如Apache、IIS 和iPlanet Web Server 等是實現HTTP 協議中的伺服器端。Web 頁由服務端資源定位,傳輸到瀏覽器,經過瀏覽器的解釋後,被客戶所看到。

Web 的工作基於客戶機/伺服器計算模型,由網頁瀏覽器(客戶機)和Web伺服器(伺服器)構成,兩者之間採用超文本傳送協議(HTTP)進行通訊。HTTP協議是Web瀏覽器和Web伺服器之間的應用程式層協議,是通用的、無狀態的、物件導向的協議。

一個完整的HTTP協議會話過程包括四個步驟:

◆ 串連,Web瀏覽器與Web伺服器建立串連,開啟一個稱為Socket(通訊端)的虛擬檔案,此檔案的建立標誌著串連建立成功;

◆ 請求,Web瀏覽器通過Socket向Web伺服器提交請求。HTTP的請求一般是GET或POST命令(POST用於FORM參數的傳遞);

◆ 應答,Web瀏覽器提交請求後,通過HTTP協議傳送給Web伺服器。Web伺服器接到後,進行交易處理,處理結果又通過HTTP傳回給Web瀏覽器,從而在Web瀏覽器上顯示出所請求的頁面;

◆ 關閉串連,應答結束後Web瀏覽器與Web伺服器必須斷開,以保證其它Web瀏覽器能夠與Web伺服器建立串連。



Java實現Web伺服器功能的程式設計


編程思路

根據上述HTTP協議的會話過程,本執行個體中實現了GET請求的Web伺服器程式的方法,方法如下:

通過建立ServerSocket 類對象,偵聽使用者指定的連接埠(為8080),等待並接受客戶機請求到連接埠。建立與Socket相關聯的輸入資料流和輸出資料流,然後讀取客戶機的請求資訊。若請求類型是GET,則從請求資訊中擷取所訪問的HTML 檔案名;如果HTML 檔案存在,則開啟HTML 檔案,把HTTP 頭資訊和HTML 檔案內容通過Socket 傳回給Web瀏覽器,然後關閉檔案,否則發送錯誤資訊給網頁瀏覽器。最後關閉與相應網頁瀏覽器串連的Socket。

用Java編寫Web伺服器httpServer.java檔案的原始碼如下:

//httpServer.java
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class httpServer{
public static void main(String args[]) {
int port;
ServerSocket server_socket;
//讀取伺服器連接埠號碼
try {
port = Integer.parseInt(args[0]);
}
catch (Exception e) {
port = 8080;
}
try {
//監聽伺服器連接埠,等待串連請求
server_socket = new ServerSocket(port);
System.out.println("httpServer running on port " +
server_socket.getLocalPort());
//顯示啟動資訊
while(true) {
Socket socket = server_socket.accept();
System.out.println("New connection accepted " +
socket.getInetAddress() +
":" + socket.getPort());
//建立分線程
try {
httpRequestHandler request =
new httpRequestHandler(socket);
Thread thread = new Thread(request);
//啟動線程
thread.start();
}
catch(Exception e) {
System.out.println(e);
}
}
}
catch (IOException e) {
System.out.println(e);
}
}
}
class httpRequestHandler implements Runnable
{
final static String CRLF = "\r\n";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
// 構造方法
public httpRequestHandler(Socket socket) throws Exception
{
this.socket = socket;
this.input = socket.getInputStream();
this.output = socket.getOutputStream();
this.br =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
// 實現Runnable 介面的run()方法
public void run()
{
try {
processRequest();
}
catch(Exception e) {
System.out.println(e);
}
}
private void processRequest() throws Exception
{
while(true) {
//讀取並顯示網頁瀏覽器提交的請求資訊
String headerLine = br.readLine();
System.out.println("The client request is "+headerLine);
if(headerLine.equals(CRLF) || headerLine.equals("")) break;
StringTokenizer s = new StringTokenizer(headerLine);
String temp = s.nextToken();
if(temp.equals("GET")) {
String fileName = s.nextToken();
fileName = "." + fileName ;
// 開啟所請求的檔案
FileInputStream fis = null ;
boolean fileExists = true ;
try
{
fis = new FileInputStream( fileName ) ;
}
catch ( FileNotFoundException e )
{
fileExists = false ;
}
// 完成回應訊息
String serverLine = "Server: a simple java httpServer";
String statusLine = null;
String contentTypeLine = null;
String entityBody = null;
String contentLengthLine = "error";
if ( fileExists )
{
statusLine = "HTTP/1.0 200 OK" + CRLF ;
contentTypeLine = "Content-type: " +
contentType( fileName ) + CRLF ;
contentLengthLine = "Content-Length: "
+ (new Integer(fis.available())).toString()
+ CRLF;
}
else
{
statusLine = "HTTP/1.0 404 Not Found" + CRLF ;
contentTypeLine = "text/html" ;
entityBody = "<HTML>" +
"<HEAD><TITLE>404 Not Found</TITLE></HEAD>" +
"<BODY>404 Not Found"
+"<br>usage:http://yourHostName:port/"
+"fileName.html</BODY></HTML>" ;
}
// 發送到伺服器資訊
output.write(statusLine.getBytes());
output.write(serverLine.getBytes());
output.write(contentTypeLine.getBytes());
output.write(contentLengthLine.getBytes());
output.write(CRLF.getBytes());
// 發送資訊內容
if (fileExists)
{
sendBytes(fis, output) ;
fis.close();
}
else
{
output.write(entityBody.getBytes());
}
}
}
//關閉通訊端和流
try {
output.close();
br.close();
socket.close();
}
catch(Exception e) {}
}
private static void sendBytes(FileInputStream fis, OutputStream os)
throws Exception
{
// 建立一個 1K buffer
byte[] buffer = new byte[1024] ;
int bytes = 0 ;
// 將檔案輸出到通訊端輸出資料流中
while ((bytes = fis.read(buffer)) != -1 )
{
os.write(buffer, 0, bytes);
}
}
private static String contentType(String fileName)
{
if (fileName.endsWith(".htm") || fileName.endsWith(".html"))
{
return "text/html";
}

return "fileName";
}
}



編程技巧說明

◆ 主線程設計

主線程的設計就是在主線程httpServer 類中實現了伺服器連接埠的偵聽,伺服器接受一個用戶端請求之後建立一個線程執行個體處理請求,代碼如下:

import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class httpServer{
public static void main(String args[]) {
port;
ServerSocket server_socket;
//讀取伺服器連接埠號碼
try {
port = Integer.parseInt(args[0]);
}
catch (Exception e) {
port = 8080;
}
try {
//監聽伺服器連接埠,等待串連請求
server_socket = new ServerSocket(port);
System.out.println("httpServer running on port "
+server_socket.getLocalPort());
..........
..........



◆ 串連處理分線程設計

在分線程httpRequestHandler 類中實現了HTTP 協議的處理,這個類實現了Runnable 介面,代碼如下:

class httpRequestHandler implements Runnable
{
final static String CRLF = "\r\n";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
// 構造方法
public httpRequestHandler(Socket socket) throws Exception
{
this.socket = socket;
//得到輸入輸出資料流
this.input = socket.getInputStream();
this.output = socket.getOutputStream();
this.br =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
}

// 實現Runnable 介面的run()方法
public void run()
{
try {
processRequest();
}
catch(Exception e) {
System.out.println(e);
}
}



◆ 構建processRequest()方法來處理資訊的接收和發送

作為實現Runnable 介面的主要內容,在run()方法中調用processRequest()方法來處理客戶請求內容的接收和伺服器返回資訊的發送,代碼如下:

private void processRequest() throws Exception
{
while(true) {
//讀取並顯示網頁瀏覽器提交的請求資訊
String headerLine = br.readLine();
System.out.println("The client request is "+ headerLine);
if(headerLine.equals(CRLF) || headerLine.equals("")) break;
//根據請求字串中的空格拆分客戶請求
StringTokenizer s = new StringTokenizer(headerLine);
String temp = s.nextToken();
if(temp.equals("GET")) {
String fileName = s.nextToken();
fileName = "." + fileName ;
.............
.............



在processRequest()方法中得到用戶端請求後,利用一個StringTokenizer 類完成了字串的拆分,這個類可以實現根據字串中指定的分隔字元(預設為空白格)將字串拆分成為字串的功能。利用nextToken()方法依次得到這些字串;sendBytes()方法完成資訊內容的發送,contentType()方法用於判斷檔案的類型。

顯示Web頁面

顯示 Web 頁面的index.html 檔案代碼如下:

<html>
<head>
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Java Web 服務器</title>
</head>
<body>
<p>********* <font color="#FF0000">歡迎你的到來!</font>*********</p>
<p>這是一個用 Java 語言實現的 Web 服務器</p>
<hr>
</body>
</html>




運行執行個體


為了測試上述程式的正確性,將編譯後的httpServer.class、httpRequestHandler.class和上面的index.html檔案置於網路的某台主機的同一目錄中。

首先運行伺服器程式 java httpServer 8080,伺服器程式運行後顯示連接埠資訊“httpServer runing on port 8080”, 然後在瀏覽器的地址欄中輸入http://localhost:8080/index.html,就可以正確顯示網頁,同時在顯示“httpServer runing on port 8080 ”視窗中伺服器會出現一些資訊。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.