欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Http學(xué)習(xí)之組裝報(bào)文

 更新時(shí)間:2023年04月28日 08:41:44   作者:CrazyDragon_King  
這篇文章主要介紹了Http學(xué)習(xí)之組裝報(bào)文,組裝報(bào)文就是指組裝HTTP響應(yīng)報(bào)文,你需要返回客戶請(qǐng)求的相應(yīng)資源,通常一個(gè)完整的報(bào)文包括報(bào)文頭和報(bào)文體,一起來看看吧

前面介紹了一些,基本的概念和需要具備的編程知識(shí)。下面開始來進(jìn)行代碼的編寫,前面已經(jīng)提到了最終的代碼會(huì)是一個(gè)的http服務(wù)器的小demo–一個(gè)圖床網(wǎng)站。

主要目標(biāo)介紹

這里主要涉及的知識(shí)點(diǎn)就是解析報(bào)文和組裝報(bào)文。 解析報(bào)文就是指解析HTTP請(qǐng)求報(bào)文,你需要知道報(bào)文請(qǐng)求的資源是什么。 組裝報(bào)文就是指組裝HTTP響應(yīng)報(bào)文,你需要返回客戶請(qǐng)求的相應(yīng)資源。

目標(biāo)分析

解析報(bào)文,需要獲取完整的報(bào)文,利用報(bào)文的特定結(jié)構(gòu),獲取報(bào)文里面的信息。然后依據(jù)這些信息,先客戶端返回響應(yīng)報(bào)文。這里涉及到自己解析報(bào)文,比較有難度,因?yàn)樾枰獔?bào)文的結(jié)構(gòu)特定。上一篇博客已經(jīng)簡單介紹過了(這里只是處理一些簡單的請(qǐng)求和響應(yīng)報(bào)文,不是那種特別復(fù)雜的,畢竟只是學(xué)習(xí),沒有必要自己和自己過不去?。?/p>

組裝報(bào)文,需要將客戶需要的信息組裝好,然后發(fā)送給客戶端。報(bào)文會(huì)由客戶端(通常是瀏覽器)自動(dòng)解析,這里就不需要解析了,只是把報(bào)文發(fā)送給客戶端。對(duì)于編程來說,只是涉及到IO流的處理而已。所以,組裝報(bào)文比較簡單一些。

所以,這篇博客就先只介紹如何組裝報(bào)文(注意這里雖然是簡單的報(bào)文,但也是符合規(guī)定的報(bào)文)。

組裝報(bào)文

我們來回顧一下前面介紹的知識(shí),通常一個(gè)完整的報(bào)文包括報(bào)文頭和報(bào)文體。(當(dāng)然了,GET請(qǐng)求方式是沒有請(qǐng)求體的。)

主要的代碼就是下面三行了。

out.write(header);        //寫入Http報(bào)文頭部部分
out.write(content);       //寫入Http報(bào)文數(shù)據(jù)部分
out.flush();              //刷新輸出流,確保緩沖區(qū)內(nèi)數(shù)據(jù)已經(jīng)發(fā)送完成

是不是感覺很神奇,所謂的HTTP報(bào)文,在TCP這個(gè)層次來看,不過就是一個(gè)字節(jié)流。(這里 header 和 content 在網(wǎng)絡(luò)上是可以看成串行的流。)

啟動(dòng)服務(wù)類

這個(gè)類和平時(shí)使用的 ServerSocket 類用法沒有什么區(qū)別,就是使用多線程來處理每一個(gè)客戶端的連接。啟動(dòng)一個(gè)ServerSocket,監(jiān)聽10000端口。

package com.dragon;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpServer {
	private static ServerSocket server;
	
	/**
	 * 啟動(dòng)服務(wù)
	 * */
	public void start() {
	    try {
	    	server = new ServerSocket(10000);
	    	System.out.println("服務(wù)啟動(dòng)成功...");
	    	this.receiveRequest();
	    } catch (IOException e) {
	    	e.printStackTrace();
	    	System.out.println("服務(wù)啟動(dòng)失敗!");
	    }
	}

	
	/**
	 * 接收請(qǐng)求
	 * */
	public void receiveRequest() {
		ExecutorService pool = Executors.newFixedThreadPool(10);
		while (true) {
			try {
				Socket client = server.accept();
				System.out.println("用戶"+client.getInetAddress().toString()+"建立連接" + client.toString());
				pool.submit(new Connection(client));  //使用線程處理每一個(gè)請(qǐng)求。
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 停止服務(wù)
	 * 注:一般是不需要關(guān)閉服務(wù)的。
	 * */
	public void stop() {
		try {
			if (server != null) {
				server.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("服務(wù)器關(guān)閉失?。?);
		}
	}
}

連接類

對(duì)于每一個(gè)客戶端的連接,獲取用戶的請(qǐng)求,并返回響應(yīng)。因?yàn)橹皇且粋€(gè)簡單的模擬,這里其實(shí)獲取用戶的請(qǐng)求也不進(jìn)行處理(因?yàn)樘幚硇枰馕稣?qǐng)求報(bào)文),對(duì)于任何的請(qǐng)求返回的響應(yīng)都是同一個(gè)。所以,它實(shí)際上還具有一個(gè)非常有趣的特點(diǎn)–消滅了404。 相信經(jīng)常使用瀏覽器的人應(yīng)該都知道404這個(gè)錯(cuò)誤吧,404的意思是對(duì)于當(dāng)前的請(qǐng)求沒有找到請(qǐng)求的資源。所以,通??梢钥吹?Not Found 這兩個(gè)英文單詞,當(dāng)然了也可以自定義成其它的形式。因?yàn)檫@個(gè)程序,只具有接收請(qǐng)求,返回響應(yīng)的基本功能,所以,我間接消滅了404,哈哈!

注:頭部字段中,我只是返回了幾個(gè)必要的頭部。因?yàn)镠TTP頭部還是比較多的,有些也不是必要的。具體的信息,可以參考一些專業(yè)的書籍來了解更多的知識(shí)或者直接閱讀這方面的權(quán)威–RFC文檔,哈哈(不過我也沒有看,就是瞅了一眼。)。

package com.dragon;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Connection implements Runnable {
	private static final String BLANK = " ";
	private static final String CRLF = "\r\n";
	private byte[] content;
	private byte[] header;
	private Socket client;
	
	public Connection(Socket client) {
		this.client = client;
	}
	
	@Override
	public void run() {
		//這里不對(duì)請(qǐng)求進(jìn)行處理,只是收到請(qǐng)求之后會(huì)進(jìn)行響應(yīng),而不管是什么請(qǐng)求
		//這里模擬服務(wù)器最原始的功能:請(qǐng)求、響應(yīng)。
		this.response();
		System.out.println("線程執(zhí)行結(jié)束了!");
	}
	
	/**
	 * 接收請(qǐng)求信息,這里只是一個(gè)簡單的模擬,這里只能接收get請(qǐng)求。
	 * @throws IOException 
	 * */
	public String getRequestInfo(InputStream in) throws IOException {
		//只讀取第一行,這是我們需要的全部內(nèi)容
		StringBuilder requestLine = new StringBuilder(80);
		while (true) {
			int c = in.read();
			if (c == '\r' || c == '\n' || c == -1) break;
			requestLine.append((char)c);
		}
		return requestLine.toString();
	}
	
	/**
	 * 響應(yīng)信息
	 * */
	public void response() {
		InputStream in = null;
		OutputStream out = null; 
		try {
			in = new BufferedInputStream(client.getInputStream());
			out = new BufferedOutputStream(client.getOutputStream());   //獲取輸出流
			String requestInfo = this.getRequestInfo(in);           //如果不讀取客戶端發(fā)來的數(shù)據(jù),服務(wù)器就會(huì)出錯(cuò)。
			System.out.println(requestInfo);
		} catch (IOException e1) {
			e1.printStackTrace();
		}       //獲取輸入流
		
		
		//響應(yīng)體數(shù)據(jù)
		File file = new File("D:/DragonFile/target/attitude.jpg");
		String contentType = null;  //文件的 MIME 類型
		try {
			content = Files.readAllBytes(file.toPath());           //使用 Files 工具類,一次性讀取文件
			contentType = Files.probeContentType(file.toPath());   //獲取文件的 MIME 類型
			long length = file.length();                           //獲取文件字節(jié)長度
			header = this.getHeader(contentType, length);          // 填充響應(yīng)頭
 		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			out.write(header);        //寫入Http報(bào)文頭部部分
			out.write(content);       //寫入Http報(bào)文數(shù)據(jù)部分
			out.flush();              //刷新輸出流,確保緩沖區(qū)內(nèi)數(shù)據(jù)已經(jīng)發(fā)送完成
			
			System.out.println("報(bào)文總大?。ㄗ止?jié)):" + (header.length + content.length));
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("客戶斷開連接或者發(fā)送失敗!");
		} finally {
			//此處關(guān)閉 client 會(huì)導(dǎo)致程序出現(xiàn)問題,但是原因不清楚。
			try {
				if (client != null) {
					client.close();
				}
				System.out.println("請(qǐng)求結(jié)束了");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
    }
	
	//響應(yīng)頭
	private byte[] getHeader(String contentType, long length) {
		return new StringBuilder()
				.append("HTTP/1.1").append(BLANK).append(200).append(BLANK).append("OK").append(CRLF)  // 響應(yīng)頭部
				.append("Server:"+"CrazyDragon").append(CRLF)
				.append("Date:").append(BLANK).append(this.getDate()).append(CRLF)
				.append("Content-Type:").append(BLANK).append(contentType).append(CRLF) //文件的 Content-Type 可通過Java獲取。
				.append("Content-Length:").append(BLANK).append(length).append(CRLF).append(CRLF)
				.toString()
				.getBytes(Charset.forName("UTF-8"));
	}
	
	//獲取時(shí)間
	private String getDate() {
		Date date = new Date();
		SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
		format.setTimeZone(TimeZone.getTimeZone("GMT")); // 設(shè)置時(shí)區(qū)為GMT  
		return format.format(date);
	}
}


主類

package com.dragon;

public class Test {
	public static void main(String[] args) {
		HttpServer httpServer = new HttpServer();
		httpServer.start();
	}
}

運(yùn)行結(jié)果

打印輸出:

在這里插入圖片描述

瀏覽器訪問:

在這里插入圖片描述

注:上面可以看到會(huì)有兩個(gè)請(qǐng)求,這是因?yàn)闉g覽器訪問的時(shí)候,會(huì)請(qǐng)求網(wǎng)站的圖標(biāo),通常的路徑為 /favicon.ico,這里似乎是因?yàn)榫彺妫瑳]有看到它的請(qǐng)求,如果存在請(qǐng)求行應(yīng)該為:GET /favicon.ico HTTP/1.1。

隨便訪問一個(gè)地址,看一看沒有404的網(wǎng)站!

在這里插入圖片描述

訪問結(jié)果

在這里插入圖片描述

注意:這里可能會(huì)遇到異常。 但是這個(gè)對(duì)于程序的運(yùn)行結(jié)果沒有影響。報(bào)錯(cuò)的原因處在這句話:out.wirte(content);

在這里插入圖片描述

可能的原因有一下幾點(diǎn):

①:服務(wù)器的并發(fā)連接數(shù)超過了其承載量,服務(wù)器會(huì)將其中一些連接Down掉;

②:客戶關(guān)掉了瀏覽器,而服務(wù)器還在給客戶端發(fā)送數(shù)據(jù);

③:瀏覽器端按了Stop 按鈕。

④:用servlet的outputstream輸出流下載圖片時(shí),當(dāng)用戶點(diǎn)擊取消也會(huì)報(bào)這個(gè)錯(cuò)誤。

總結(jié)

目前,組裝報(bào)文這個(gè)功能已經(jīng)實(shí)現(xiàn)了,現(xiàn)在已經(jīng)很有意思了吧。通過這個(gè)簡單的程序,已經(jīng)可以真正理解為什么HTTP是建立在TCP協(xié)議之上的了吧。這里我特別選了一張圖片來進(jìn)行展示,效果應(yīng)該還是很不錯(cuò)的。

到此這篇關(guān)于Http學(xué)習(xí)之組裝報(bào)文的文章就介紹到這了,更多相關(guān)Http組裝報(bào)文內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot項(xiàng)目jar發(fā)布后如何獲取jar包所在目錄路徑

    SpringBoot項(xiàng)目jar發(fā)布后如何獲取jar包所在目錄路徑

    這篇文章主要介紹了SpringBoot項(xiàng)目jar發(fā)布后如何獲取jar包所在目錄路徑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫

    Java使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫

    在開發(fā)中,我們經(jīng)常會(huì)遇到這樣的需求,將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫中,這篇文章主要來和大家講講Java如何使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫,感興趣的可以了解下
    2024-01-01
  • SpringBoot集成redis實(shí)現(xiàn)共享存儲(chǔ)session

    SpringBoot集成redis實(shí)現(xiàn)共享存儲(chǔ)session

    這篇文章主要介紹了SpringBoot集成redis實(shí)現(xiàn)共享存儲(chǔ)session的流程步驟,文中通過代碼示例介紹的非常詳細(xì),并總結(jié)了一些常見的錯(cuò)誤及解決方法,需要的朋友可以參考下
    2024-03-03
  • Aop動(dòng)態(tài)代理和cglib實(shí)現(xiàn)代碼詳解

    Aop動(dòng)態(tài)代理和cglib實(shí)現(xiàn)代碼詳解

    這篇文章主要介紹了Aop動(dòng)態(tài)代理和cglib實(shí)現(xiàn)代碼詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • MyBatis-Plus中最簡單的查詢操作教程(Lambda)

    MyBatis-Plus中最簡單的查詢操作教程(Lambda)

    這篇文章主要給大家介紹了關(guān)于MyBatis-Plus中最簡單的查詢操作的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-03-03
  • log4j2的異步使用及添加自定義參數(shù)方式

    log4j2的異步使用及添加自定義參數(shù)方式

    這篇文章主要介紹了log4j2的異步使用及添加自定義參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java中Array、List、Map相互轉(zhuǎn)換的方法詳解

    Java中Array、List、Map相互轉(zhuǎn)換的方法詳解

    這篇文章主要介紹了Java中Array、List、Map相互轉(zhuǎn)換的方法詳解,在實(shí)際項(xiàng)目開發(fā)中或者一些算法面試題目中經(jīng)常需要用到Java中這三種類型的相互轉(zhuǎn)換,比如對(duì)于一個(gè)整型數(shù)組中尋找一個(gè)整數(shù)與所給的一個(gè)整數(shù)值相同,需要的朋友可以參考下
    2023-08-08
  • maven安裝與環(huán)境配置圖文教程

    maven安裝與環(huán)境配置圖文教程

    這篇文章主要為大家詳細(xì)介紹了maven安裝與環(huán)境配置圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 基于Apache組件分析對(duì)象池原理的實(shí)現(xiàn)案例分析

    基于Apache組件分析對(duì)象池原理的實(shí)現(xiàn)案例分析

    本文從對(duì)象池的一個(gè)簡單案例切入,主要分析common-pool2組件關(guān)于:池、工廠、配置、對(duì)象管理幾個(gè)角色的源碼邏輯,并且參考其在Redis中的實(shí)踐,對(duì)Apache組件分析對(duì)象池原理相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-04-04
  • IDEA?中配置?Gradle?和安裝使用教程

    IDEA?中配置?Gradle?和安裝使用教程

    Gradle本身是基于Groovy腳本語言進(jìn)行構(gòu)建的,并通過 Domain Specific Language(DSL 語言)進(jìn)行描述和控制構(gòu)建邏輯,本文重介紹了IDEA?中配置?Gradle?和使用,需要的朋友可以參考下
    2023-10-10

最新評(píng)論