瀏覽器與服務端的通信01
在pom.xml中導入兩個jar包:jaxen1.1.4和dom4j1.6.1
WebServer類:
/**
* 基于Http協議的Web服務端程序
* @author live
*/
public class WebServer {
private ServerSocket server;
/*
* 線程池,管理用于處理客戶端請求的線程
*/
private ExecutorService threadPool;
/**
* 構造方法,用于初始化服務端程序
* @throws Exception
*/
public WebServer() throws Exception{
try{
server=new ServerSocket(ServerContent.ServerPort);
threadPool=Executors.newFixedThreadPool(ServerContent.max_thread);
}catch(Exception e){
throw e;
}
}
public void start(){
try{
while(true){
System.out.println("等待客戶端連接");
Socket socket=server.accept();
System.out.println("客戶端已連接");
/*
* 將處理該客戶端請求的任務交給線程池
*/
threadPool.execute(new ClientHandler(socket));
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
WebServer server=new WebServer();
server.start();
}catch(Exception e){
e.printStackTrace();
System.out.println("服務端啟動失敗!");
}
}
}
ClientHandler類:
/**
* 處理客戶端請求
* @author live
*
*/
public class ClientHandler implements Runnable{
private Socket socket;
public ClientHandler(Socket socket){
this.socket=socket;
}
public void run(){
try{
/*
* HTTP協議中的請求信息格式:
* 請求行 資源 協議 換行
* GET/index.html HTTP1.1 CRLF(請求方式) POST
* 消息報頭
* 消息正文
*
* 以行為單位發送至服務端 每行結尾以(CR LF)
* CR:回車 LF:換行
* http://localhost:8080
*/
//獲取輸入流,讀取客戶端發送過來的數據
InputStream in=socket.getInputStream();
OutputStream out=socket.getOutputStream();
//讀取一行字符串,請求行信息
//Method Request-URI Http-version(CRLF)
//如:
//GET/index.html HTTP/1.1CRLF
//生成HttpRequest表示請求信息
HttpRequest request=new HttpRequest(in);
HttpResponse response=new HttpResponse(out);
if(request.getUri()!=null){
File file=new File(ServerContent.web_root+request.getUri());
if(file.exists()){
//設置狀態行
response.setStatus(HttpContent.STATUS_CODE_OK);
//設置響應頭
response.setContentType(getContentTypeByFile(file));
responseFile(file,response);
response.setContentLength((int)file.length());
System.out.println("響應客戶端完畢!");
}else{
//響應客戶端沒有該資源(顯示個錯誤頁面)
response.setStatus(HttpContent.STATUS_CODE_NOT_FOUND);
//錯誤頁面
file=new File(ServerContent.web_root+File.separator+ServerContent.notFoundPage);//"/notfound.html"
response.setContentType(getContentTypeByFile(file));
response.setContentLength((int)file.length());
responseFile(file,response);
}
}
}catch(Exception e){
e.printStackTrace();
/*
* 505
*/
}finally{
try{
socket.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 根據給定的文件獲取對應的Content-Type
* @param file
* @return
*/
private String getContentTypeByFile(File file){
/**
* 1:首先獲取該文件的名字
* 2:獲取該文件名的文件類型(文件名的后綴)
* 3:根據類型名從ServerContent的types中作為key獲取對應的Content-Type
*/
String filename=file.getName();
String ext=filename.substring(
filename.lastIndexOf(".")+1);
String contentType=ServerContent.types.get(ext);
System.out.println("contentType:"+contentType);
return contentType;
}
/**
* 響應文件
* @param file
* @param response
* @throws Exception
*/
public void responseFile(File file,HttpResponse res) throws Exception{
BufferedInputStream bis=null;
try{
/*
* 將該文件中每個字節通過客戶端輸出流發送給服務端
*/
bis=new BufferedInputStream(
new FileInputStream(file));
/*
* 通過Response取出輸出流,這里就自動發發送
* 了狀態行和響應頭
*/
OutputStream out=res.getOutputStream();
int d=-1;
while((d=bis.read())!=-1){
out.write(d);
}
}catch(IOException e){
throw e;
}finally{
if(bis!=null){
bis.close();
}
}
}
}
HttpContent類:
/**
* 定義HTTP協議中的相關信息
* @author live
*
*/
public class HttpContent {
/*
* 回車
*/
public static final int CR=13;
/*
* 換行
*/
public static final int LF=10;
/**
* 狀態碼:成功
*/
public static final int STATUS_CODE_OK=200;
/**
* 狀態描述:成功
*/
public static final String STATUS_VALUE_OK="Ok";
/**
* 狀態代碼:未找到
*/
public static final int STATUS_CODE_NOT_FOUND=404;
/**
* 狀態描述:未找到
*/
public static final String STATUS_VALUE_NOT_FOUND="Not Found";
/**
* 狀態代碼:錯誤
*/
public static final int STATUS_CODE_ERROR=500;
/**
* 狀態描述:錯誤
*/
public static final String STATUS_VALUE_ERROR="Internal Server Error";
}
ServerContent類:
/**
* 記錄服務端相關信息
* @author live
*/
public class ServerContent {
/*
* 服務端口
*/
public static int ServerPort;
//服務端最大并發數
public static int max_thread;
//服務端應用根目錄
public static String web_root;
/*
* 服務器使用的Http協議的版本
*/
public static String protocol;
//根據媒體文件后綴對應Content-Type的類型
public static Map<String,String> types=new HashMap<String,String>();
/*
* 404頁面
*/
public static String notFoundPage;
static{
//初始化 ServerContent的靜態
init();
}
/**
* 加載server.xml文件對ServerContent進行初始化
*/
private static void init(){
try{
SAXReader reader=new SAXReader();
Document doc=reader.read(new FileInputStream(
"config"+File.separator+"server.xml"));
Element root=doc.getRootElement();
//解析<service>
Element serviceEle=root.element("service");
//解析<connector>
Element connEle=serviceEle.element("connector");
protocol=connEle.attributeValue("protocol");
System.out.println("protocol:"+protocol);
ServerPort=Integer.parseInt(
connEle.attributeValue("port"));
System.out.println(" ServerPort:"+ ServerPort);
max_thread=Integer.parseInt(
connEle.attributeValue("maxThread"));
System.out.println(" max_thread:"+ max_thread);
//解析<webroot>
web_root=serviceEle.elementText("webroot");
System.out.println(" web_root:"+ web_root);
//解析<not-found-page>
notFoundPage=serviceEle.elementText("not-found-page");
System.out.println("not-found-page:"+ notFoundPage);
/*
* 解析<type-mappings>
*/
Element mappingsEle=root.element("type-mappings");
List<Element> mappingsList=mappingsEle.elements();
for(Element mapping:mappingsList){
types.put(mapping.attributeValue("ext"),
mapping.attributeValue("type"));
}
System.out.println("types:"+types);
}catch(Exception e){
e.printStackTrace();
}
}
}
HttpRequest類:
/*
* 封裝Http請求相關內容
*/
public class HttpRequest {
//請求方法
private String method;
//請求資源
private String uri;
//請求協議
private String protocol;
/*
* 構造方法,用于創建HttpRequest實例
*
* @param in 對應客戶端輸入流,通過該流讀取客
* 戶端發送過來的請求信息并封裝到當前HttpRequest對象中
*/
public HttpRequest(InputStream in){
/*
* HTTP協議中的請求信息格式:
* 請求行 資源 協議 換行
* GET/index.html HTTP1.1 CRLF(請求方式) POST
* 消息報頭
* 消息正文
*
* 以行為單位發送至服務端 每行結尾以(CR LF)
* CR:回車 LF:換行
* http://localhost:8080
*/
try{
//讀取一行字符串,請求行信息
//Method Request-URI Http-version(CRLF)
//如:
//GET/index.html HTTP/1.1CRLF
String line=readline(in);
if(line.length()>0){
String[] data=line.split("\\s");
method=data[0];
uri=data[1];
protocol=data[2];
}
}catch(IOException e){
e.printStackTrace();
}
}
/**
* 從給定的輸入流中讀取一行字符竄并返回。
* 當讀取到CR LF時認為一行結束
* 13 10
*
* @param in
* @return
* @throws IOException
*/
private String readline(InputStream in)throws IOException{
/*
* 順序的從流中讀取每個字節并轉換為對應的字符
* 然后拼接在一起,直到連續讀取了CR LF時停止
* 并將拼接的字符串返回
*/
StringBuilder builder=new StringBuilder();
try{
int ch1=-1,ch2=-1;
while((ch2=in.read())!=-1){
if(ch1==HttpContent.CR&&ch2==HttpContent.LF){
break;
}
builder.append((char)ch2);
ch1=ch2;
}
return builder.toString().trim();
}catch(IOException e){
throw e;
}
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
}
HttpResponse類:
/**
* 封裝Http響應
* HTTP響應格式
* 1:狀態行
* 2:響應頭
* 3:響應正文
*
* 狀態由3部分組成:協議版本,數字形式的狀態代碼,狀態描述
* 格式:Http_Version Status-code Reason-Phrase CRLF
*
* 例如:HTTP/1.1 200 OK CRLF
*
* 狀態代碼第一個數字有5種:
* 1xx:指示信息,表示請求已接收,繼續處理。
* 2xx:成功,表示請求已接收,理解,接受
* 3xx:重定向,要完成請求需要更進一步的操作
* 4xx:客戶端錯誤,請求語法錯誤或請求無法實現
* 5xx:服務端錯誤,服務端未能實現該請求
* 常見的狀態碼及描述:
* 200 OK 客戶端請求成功
* 400 Bad Request 客戶端請求有語法錯誤,服務端不能理解
* 401 Unauthonzed 請求未授權
* 403 Forbidden 服務端收到請求,但是拒絕提供服務
* 404 Not Found 請求的資源不存在
* 500 Internal Server Error 服務器發生了不可預期的錯誤
* 503 Service Unavailable 服務器當前不能夠處理客戶端請求
*
* 回應客戶端對應的資源
* HTTP中響應的格式:
* 1:狀態行
* 2:響應頭
* 3:響應正文
* 狀態行格式:
* HTTP-Version Status_code Reason_phrase CRLF
* HTTP協議 狀態代碼 狀態描述CRLF
* 例如:
* HTTP/1.1 200 OK CRLF
* 2:響應頭
* 響應頭注明很多返回的信息,按行輸出
* 常見:
* Content-Type:用來指明發送給接收者的媒體類型
* 常見的Content-Type:
* text/html:HTML格式文件(網頁)
* text/xml:XML格式文件
* image/gif:gif格式圖片
* image/jpeg:jpeg格式圖片
* image/png:png格式圖片
* Content-Length,用來指明發送給接受者實體正文的長度
* 簡單的說就是發送過去的數據的字節量
*
* HTTP協議要求實際響應客戶端時的數據格式:
* HTTP/1.1 200 OK CRLF 狀態行
* Content-Type:text/html CRLF 響應頭信息
* Content-Length:100CRLF 響應頭信息
* CRLF 單獨發送CRLF指明響應頭全部發送完畢
* DATA 實際數據
* TLD type length data
*
* @author live
*/
public class HttpResponse {
private OutputStream out;
/*
* 響應格式中所需要的信息
*/
private int status;//狀態行中的狀態代碼
private String contentType;//響應頭中的正文類型
private int contentLength;//響應頭中的正文長度
//表示響應狀態行以及響應頭信息是否已經發送過
private boolean hasPrintHeader;
//記錄所有可用狀態碼及描述。
private Map<Integer,String> statusMap;
/*
* 構造方法
* @param out 對應客戶端的輸出流,通過該輸出流
* 將消息響應給客戶端
*/
public HttpResponse(OutputStream out){
this.out=out;
statusMap=new HashMap<Integer,String>();
statusMap.put(HttpContent.STATUS_CODE_OK,HttpContent.STATUS_VALUE_OK);
statusMap.put(HttpContent.STATUS_CODE_NOT_FOUND,HttpContent.STATUS_VALUE_NOT_FOUND);
statusMap.put(HttpContent.STATUS_CODE_ERROR,HttpContent.STATUS_VALUE_ERROR);
}
public OutputStream getOutputStream() throws Exception {
if(!hasPrintHeader){
/*
* 獲取輸出流前,將狀態行,響應頭信息自動發送
*/
//發送狀態行
println(ServerContent.protocol+" "+status+" "+statusMap.get(status));
//發送響應頭信息
println("ContentType:"+contentType);
println("ContentLength:"+contentLength);
println("");//單獨發送空行表示響應頭發送完畢
hasPrintHeader=true;
}
return out;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public int getContentLength() {
return contentLength;
}
public void setContentLength(int contentLength) {
this.contentLength = contentLength;
}
public Map<Integer, String> getStatusMap() {
return statusMap;
}
/*
* 將給定的一行字符串以HTTP協議格式要求按行寫出
*/
private void println(String str) throws Exception{
try {
out.write(str.getBytes("ISO8859-1"));
out.write(HttpContent.CR);
out.write(HttpContent.LF);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
System.out.println("不受支持的編碼");
throw e;
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
}
server配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<server>
<service>
<connector protocol="HTTP/1.1" port="8888" maxThread="100"/>
<webroot>webapps</webroot>
<not-found-page>notfound.html</not-found-page>
</service>
<!-- 不同媒體類型對應的Content-Type值 -->
<type-mappings>
<type-mapping ext="html" type="text/html"/>
<type-mapping ext="png" type="image/png"/>
<type-mapping ext="xml" type="text/xml"/>
<type-mapping ext="jpeg" type="image/jpeg"/>
<type-mapping ext="gif" type="image/gif"/>
<type-mapping ext="ico" type="image/ico"/>
</type-mappings>
</server>
智能推薦
websocketj--隨時隨地在Web瀏覽器中操作你的服務端程序
0 - 有沒有覺得Linux標準終端界面輸入輸出枯燥無味? 1 - 什么?vmstat命令的輸出數據不直觀?有沒有想過能夠可視化該命令的輸出? 2 - 嘗試過用瀏覽器操作Windows中的cmd嗎? websocketj可以解決以上所有問題,讓你隨時隨地通過瀏覽器訪問任何平臺上的應用程序。 websocketj是什么? 如何使用websocketj? websocketj是如何工作的? 在瀏覽器中...
1.瀏覽器與web服務器的通信原理概述
TCP/IP、以太網等技術,是一種規定網絡物理設備和軟件如何工作的一種規則,僅僅學習這些規則是無法看到設備和軟件的內部構造的,本書將重點介紹其內部工作方式 網絡實際上由兩大部分組成: 1. 傳輸信息的機制:網絡控制軟件,交換機、路由器登硬件 2. 瀏覽器、web服務器這些網絡應用程序 目錄 1.1 生成HTTP請求消息 1.2 向DNS服務器查詢web服務器的IP地址 1.3 DNS...
3D游戲編程與設計——游戲對象與圖形基礎章節作業與練習
3D游戲編程與設計——游戲對象與圖形基礎章節作業與練習 3D游戲編程與設計——游戲對象與圖形基礎章節作業與練習 自學資源 作業內容 1、基本操作演練【建議做】 天空盒的制作: 地圖的制作: 整體效果: 2、編程實踐 項目要求: 項目結構: 代碼詳解: Actions: ISSActionCallback.cs SSAction.cs SSAction...
猜你喜歡
FlycoTabLayout 的使用
FlycoTabLayout 一個Android TabLayout庫,目前有3個TabLayout SlidingTabLayout:參照PagerSlidingTabStrip進行大量修改. 新增部分屬性 新增支持多種Indicator顯示器 新增支持未讀消息顯示 新增方法for懶癌患者 CommonTabLayout:不同于SlidingTabLayout對ViewPager依賴,它是一個不...
爬蟲項目實戰八:爬取天氣情況
爬取天氣情況 目標 項目準備 接口分析 代碼實現 效果顯示 寫入本地 目標 根據天氣接口,爬取接下來一周的天氣情況。 項目準備 軟件:Pycharm 第三方庫:requests,BeautifulSoup,csv 接口地址:http://api.k780.com:88/?app=weather.future&weaid=城市名&appkey=10003&sign=b59bc...
關于web項目的目錄問題
先給段代碼: 上面這個代碼一直出錯,我不知道原因,后面不停的查找資料發現了問題:我的web項目輸出目錄有問題,因為我也是第一次用idea寫web項目,發現很多bug 其實都沒有太大問題,我們需要注意的是你必須在out這個輸出文件夾中擁有這個文件,out輸出文件夾會默認過濾這些文件...
二叉搜索樹轉化為雙向鏈表
題目描述: 輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。 為了讓您更好地理解問題,以下面的二叉搜索樹為例: 我們希望將這個二叉搜索樹轉化為雙向循環鏈表。鏈表中的每個節點都有一個前驅和后繼指針。對于雙向循環鏈表,第一個節點的前驅是最后一個節點,最后一個節點的后繼是第一個節點。 下圖展示了上面的二叉搜索樹轉化成的鏈表。&ldqu...
Cocos2d-x 2.0 網格動畫深入分析
[Cocos2d-x相關教程來源于紅孩兒的游戲編程之路CSDN博客地址:http://blog.csdn.net/honghaier] 紅孩兒Cocos2d-X學習園地QQ2群:44208467加群寫:Cocos2d-x 紅孩兒Cocos2d-X學習園地QQ群:249941957[暫滿]加群寫:Cocos2d-x 本章為我的Cocos2d-x教程一書初稿。望各位看官多提建議! Cocos2d-x ...