最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

主要目標(biāo)介紹

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

目標(biāo)分析

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

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

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

組裝報文

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

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

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

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

啟動服務(wù)類

這個類和平時使用的 ServerSocket 類用法沒有什么區(qū)別,就是使用多線程來處理每一個客戶端的連接。啟動一個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;
	
	/**
	 * 啟動服務(wù)
	 * */
	public void start() {
	    try {
	    	server = new ServerSocket(10000);
	    	System.out.println("服務(wù)啟動成功...");
	    	this.receiveRequest();
	    } catch (IOException e) {
	    	e.printStackTrace();
	    	System.out.println("服務(wù)啟動失??!");
	    }
	}

	
	/**
	 * 接收請求
	 * */
	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));  //使用線程處理每一個請求。
			} 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)閉失敗!");
		}
	}
}

連接類

對于每一個客戶端的連接,獲取用戶的請求,并返回響應(yīng)。因為只是一個簡單的模擬,這里其實獲取用戶的請求也不進(jìn)行處理(因為處理需要解析請求報文),對于任何的請求返回的響應(yīng)都是同一個。所以,它實際上還具有一個非常有趣的特點–消滅了404。 相信經(jīng)常使用瀏覽器的人應(yīng)該都知道404這個錯誤吧,404的意思是對于當(dāng)前的請求沒有找到請求的資源。所以,通??梢钥吹?Not Found 這兩個英文單詞,當(dāng)然了也可以自定義成其它的形式。因為這個程序,只具有接收請求,返回響應(yīng)的基本功能,所以,我間接消滅了404,哈哈!

注:頭部字段中,我只是返回了幾個必要的頭部。因為HTTP頭部還是比較多的,有些也不是必要的。具體的信息,可以參考一些專業(yè)的書籍來了解更多的知識或者直接閱讀這方面的權(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() {
		//這里不對請求進(jìn)行處理,只是收到請求之后會進(jìn)行響應(yīng),而不管是什么請求
		//這里模擬服務(wù)器最原始的功能:請求、響應(yīng)。
		this.response();
		System.out.println("線程執(zhí)行結(jié)束了!");
	}
	
	/**
	 * 接收請求信息,這里只是一個簡單的模擬,這里只能接收get請求。
	 * @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ù)器就會出錯。
			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報文頭部部分
			out.write(content);       //寫入Http報文數(shù)據(jù)部分
			out.flush();              //刷新輸出流,確保緩沖區(qū)內(nèi)數(shù)據(jù)已經(jīng)發(fā)送完成
			
			System.out.println("報文總大?。ㄗ止?jié)):" + (header.length + content.length));
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("客戶斷開連接或者發(fā)送失?。?);
		} finally {
			//此處關(guān)閉 client 會導(dǎo)致程序出現(xiàn)問題,但是原因不清楚。
			try {
				if (client != null) {
					client.close();
				}
				System.out.println("請求結(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"));
	}
	
	//獲取時間
	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è)置時區(qū)為GMT  
		return format.format(date);
	}
}


主類

package com.dragon;

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

運行結(jié)果

打印輸出:

在這里插入圖片描述

瀏覽器訪問:

在這里插入圖片描述

注:上面可以看到會有兩個請求,這是因為瀏覽器訪問的時候,會請求網(wǎng)站的圖標(biāo),通常的路徑為 /favicon.ico,這里似乎是因為緩存,沒有看到它的請求,如果存在請求行應(yīng)該為:GET /favicon.ico HTTP/1.1。

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

在這里插入圖片描述

訪問結(jié)果

在這里插入圖片描述

注意:這里可能會遇到異常。 但是這個對于程序的運行結(jié)果沒有影響。報錯的原因處在這句話:out.wirte(content);

在這里插入圖片描述

可能的原因有一下幾點:

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

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

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

④:用servlet的outputstream輸出流下載圖片時,當(dāng)用戶點擊取消也會報這個錯誤。

總結(jié)

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

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

相關(guān)文章

  • Java簡化復(fù)雜系統(tǒng)調(diào)用的門面設(shè)計模式

    Java簡化復(fù)雜系統(tǒng)調(diào)用的門面設(shè)計模式

    Java門面模式是一種結(jié)構(gòu)性設(shè)計模式,它為復(fù)雜系統(tǒng)提供了一個簡單的接口,使得系統(tǒng)的客戶端能夠更加方便地使用系統(tǒng)功能。門面模式通過封裝復(fù)雜的子系統(tǒng),隱藏系統(tǒng)的實現(xiàn)細(xì)節(jié),提高了系統(tǒng)的易用性和靈活性
    2023-04-04
  • dependencies導(dǎo)致的Maven依賴出錯包紅問題解決方法

    dependencies導(dǎo)致的Maven依賴出錯包紅問題解決方法

    多模塊和分布式開發(fā)一般都是有專門的的dependencies來進(jìn)行jar包的版本依賴問題,本文主要介紹了dependencies導(dǎo)致的Maven依賴出錯包紅問題解決方法,具有一定的參考價值,感興趣的可以了解一下
    2022-05-05
  • executor包執(zhí)行器功能

    executor包執(zhí)行器功能

    這篇文章主要介紹了executor包執(zhí)行器功能,executor包中的各個子包提供的功能,最終這些功能都由Executor接口及其實現(xiàn)類共同對外提供服務(wù)。下文介紹該執(zhí)行功能,具有一定的參考價值,需要的朋友可以考一下
    2022-02-02
  • Java刪除文件、目錄及目錄下所有文件的方法實例

    Java刪除文件、目錄及目錄下所有文件的方法實例

    這篇文章主要給大家介紹了關(guān)于利用Java刪除文件、目錄及目錄下所有文件的方法,文中給出了詳細(xì)的示例代碼與注解,有需要的朋友可以參考借鑒,下面來一起看看吧。
    2016-12-12
  • 詳解ssh框架原理及流程

    詳解ssh框架原理及流程

    在本文中小編給大家整理的是關(guān)于ssh框架原理及流程的相關(guān)知識點內(nèi)容,有此需要的朋友們可以學(xué)習(xí)下。
    2019-07-07
  • 在Spring Boot2中使用CompletableFuture的方法教程

    在Spring Boot2中使用CompletableFuture的方法教程

    這篇文章主要給大家介紹了關(guān)于在Spring Boot2中使用CompletableFuture的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧
    2019-01-01
  • java string類方法深入解析

    java string類方法深入解析

    以下是對java中的string類方法進(jìn)行了詳細(xì)的分析介紹。需要的朋友可以過來參考下
    2013-08-08
  • java實現(xiàn)響應(yīng)重定向發(fā)送post請求操作示例

    java實現(xiàn)響應(yīng)重定向發(fā)送post請求操作示例

    這篇文章主要介紹了java實現(xiàn)響應(yīng)重定向發(fā)送post請求操作,結(jié)合實例形式分析了java請求響應(yīng)、重定向及數(shù)據(jù)處理相關(guān)操作技巧,需要的朋友可以參考下
    2020-04-04
  • MyBatis學(xué)習(xí)教程(四)-如何快速解決字段名與實體類屬性名不相同的沖突問題

    MyBatis學(xué)習(xí)教程(四)-如何快速解決字段名與實體類屬性名不相同的沖突問題

    我們經(jīng)常會遇到表中的字段名和表對應(yīng)實體類的屬性名稱不一定都是完全相同的情況,如何解決呢?下面腳本之家小編給大家介紹MyBatis學(xué)習(xí)教程(四)-如何快速解決字段名與實體類屬性名不相同的沖突問題,一起學(xué)習(xí)吧
    2016-05-05
  • SpringBoot+MinIO實現(xiàn)文件切片極速詳解

    SpringBoot+MinIO實現(xiàn)文件切片極速詳解

    在現(xiàn)代Web應(yīng)用中,文件上傳是一個常見的需求,尤其是對于大文件的上傳,如視頻、音頻或大型文檔,所以本文就來為大家介紹一下如何使用Spring Boot和MinIO實現(xiàn)文件切片極速上傳技術(shù)吧
    2023-12-12

最新評論

吉安市| 临桂县| 定南县| 沁源县| 上饶市| 和硕县| 丰镇市| 灵台县| 大兴区| 彭州市| 浦江县| 怀仁县| 百色市| 永康市| 裕民县| 西和县| 峡江县| 包头市| 万州区| 翼城县| 武功县| 阳高县| 特克斯县| 吉隆县| 石门县| 洱源县| 蚌埠市| 富源县| 卢龙县| 阿尔山市| 四川省| 珠海市| 安岳县| 五家渠市| 益阳市| 上蔡县| 荔浦县| 罗源县| 宁国市| 乌兰浩特市| 寿阳县|