日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

解读HTTP包

發布時間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 解读HTTP包 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

解讀HTTP包:[摘要]詳細介紹HTTP包的格式、協議內容及相關的處理方法,內容分3節:1、超文本傳輸協議及HTTP包;2、Socket類與ServerSocket類;3、讀取HTTP包。

一、超文本傳輸協議及HTTP包
HTTP協議用于在Internet上發送和接收消息。HTTP協議是一種請求-應答式的協議——客戶端發送一個請求,服務器返回該請求的應答,所有的請求與應答都是HTTP包。HTTP協議使用可靠的TCP連接,默認端口是80。HTTP的第一個版本是HTTP/0.9,后來發展到了HTTP/1.0,現在最新的版本是HTTP/1.1。HTTP/1.1由RFC 2616 定義。

在HTTP中,Client/Server之間的會話總是由客戶端通過建立連接和發送HTTP請求包初始化,服務器不會主動聯系客戶端或要求與客戶端建立連接。瀏覽器和服務器都可以隨時中斷連接,例如,在瀏覽網頁時你可以隨時點擊"停止"按鈕中斷當前的文件下載過程,關閉與Web服務器的HTTP連接。

1 HTTP請求包
HTTP請求包(GET、POST等請求方法)由三個部分構成,分別是:方法-URI-協議/版本,請求頭,請求正文。下面是一個HTTP請求包(GET)的例子:

GET /index.jsp HTTP/1.1
Accept-Language: zh-cn
Connection: Keep-Alive
Host: 192.168.0.106
Content-Length: 37

userName=new_andy&password=new_andy


請求包的第一行是方法-URI-協議/版本:
GET就是請求方法,根據HTTP標準,HTTP請求可以使用多種請求方法。HTTP 1.1支持七種請求方法:GET、POST、HEAD、OPTIONS、PUT、delete和TRACE等,常用的為請求方法是GET和POST。

/index.jsp 表示URI。URI指定了要訪問的網絡資源。
HTTP/1.1是協議和協議的版本。
最后一行userName=new_andy& amp;password=new_andy為正文,正文與HTTP頭部有一個空行(\r\n)分隔。這里需要說明的一點,其中Content- Length說明正文的長度,有的正文長度沒有在頭部說明,只是標明Transfer-Encoding: chunked。關于chunked類型的長度計算方法,見RFC 1626。
請求包的頭部還會包含許多有關客戶端環境和請求正文的有用信息,這里不再描述。

2 HTTP應答包
和HTTP請求包相似,由三個部分構成,分別是:協議-狀態代碼-描述,應答頭,應答正文。下面是一個HTTP應答的例子:

HTTP/1.1 200 OK
Server: Microsoft-IIS/4.0
Date: Mon, 3 Jan 2005 13:13:33 GMT
Content-Type: text/html
Last-Modified: Mon, 11 Jan 2004 13:23:42 GMT
Content-Length: 90

<html>
<head>
<title> 解讀HTTP包示例</title></head><body>
Hello WORLD!
</body>
</html>


HTTP應答包的第一行類似于HTTP請求的第一行,表示所用的協議是HTTP 1.1,服務器處理請求的狀態碼200。
應答頭也和請求頭一樣包含許多有用的信息,例如服務器類型、日期時間、內容類型和長度等。應答的正文就是服務器返回的HTML頁面。應答頭和正文之間也用 CRLF分隔。

二、Socket類與ServerSocket類
在Java中,通信端點由java.net.Socket類(客戶端)或java.net.ServerSocket類(服務器端)表示。應用程序通過端點向網絡發送或從網絡讀取數據。位于兩臺不同機器上的應用軟件通過網絡連接發送和接收字節流,從而實現通信。要把HTTP包發送給另一個應用,首先要知道對方的IP地址以及其通信端點的端口號。

Socket類代表的是客戶端,它是一個連接遠程服務器應用時臨時創建的端點。
ServerSocker類代表的是服務器端,它啟動后等待來自客戶端的連接請求;一旦接收到請求,ServerSocket創建一個Socket實例來處理與該客戶端的通信。對于服務器應用,我們不知道客戶端應用什么時候會試圖連接服務器,服務器必須一直處于等待連接的狀態。

下面是ServerSocket提供了四個構造函數,常用的構造函數的的一種形式為:

public ServerSocket(int port, int backLog, InetAddress bindingAddress);


參數:port指定服務器端監聽客戶端的端口;
backlog為連接請求的最大隊列長度,一旦超越這個長度,服務器端點開始拒絕客戶端的連接請求。
bindingAddress是一個java.net.InetAddress的實例,指定綁定IP地址。
創建好 ServerSocket實例之后,調用它的accept方法,要求它等待傳入的連接請求。只有出現了連接請求時,accept方法才會返回,它的返回值是一個Socket類的實例。隨后,這個Socket對象就可以用來與客戶端應用通信。

Socket類有許多構造函數,常用的為:

public Socket(String host, int port)。


參數是主機名稱(IP地址或域名)和端口號。
參數host是遠程機器的名字或IP地址,port是遠程應用的端口號。
成功創建了Socket類的實例之后,我們就可以用它來發送和接收字節流形式的數據,數據一般為HTTP包。

要發送字節流,首先要調用 Socket類的getOutputStream方法獲得一個java.io.OutputStream對象;要從連接的另一端接收字節流,首先要調用 Socket類的getInputStream方法獲得一個java.io.InputStream對象。

下面的代碼片斷創建一個與本地HTTP服務器(127.0.0.1代表本地主機的IP地址)通信的Socket,發送一個HTTP請求包,準備接收服務器的應答。

Socket socket = new Socket("127.0.0.1", "80");
OutputStream os = socket.getOutputStream();
InputStream ins = socket.getInputStream();
StringBuffer sb=new StringBuffer();
sb.append("GET /index.jsp HTTP/1.1\r\n");//注意\r\n為回車換行
sb.append("Accept-Language: zh-cn\r\n");
sb.append("Connection: Keep-Alive\r\n");
sb.append("Host: 192.168.0.106\r\n");
sb.append("Content-Length: 37\r\n");
sb.append("\r\n");
sb.append("userName=new_andy&password=new_andy\r\n");
sb.append("\r\n");

// 向Web服務器發送一個HTTP請求包
os.write(sb.toString().getBytes());

服務器端的代碼在大致結構為:
while (!shutdown) {
Socket socket = null;
try {
socket = serverSocket.accept(); //等待客戶以送HTTP請求包
// 創建HTTP請求包處理線程
RequestThread request = new RequestThread(socket);
request.start();
if(shutdown) System.exit(0);
}
catch (Exception e) {
e.printStackTrace();
}
}



RequestThread線程分析HTTP請求包,跟根據請求包內容在服務端生成一個HTTP應答包。下一節說明怎樣分析 HTTP包。
InputStream input = socket.getInputStream(); //從此字節數據流獲得HTTP請求包內容
OutputStream output= socket.getOutputStream(); //向此字節流寫入HTTP應答包內容

三、讀取HTTP包
以下是一個讀取HTTP包的類 SocketRequest。

public class SocketRequest { //從指定的Socket的InputStream中讀取數據

private InputStream input;
private String uri;
private StringBuffer request = new StringBuffer(); //用于保存所有內容
private int CONTENT_LENGTH = 0; //實際包內容數據長
private boolean bePost = false;
private boolean beHttpResponse = false;
private boolean beChucked = false;
private boolean beGet = false;
private byte crlf13 = (byte) 13; //'\r'
private byte crlf10 = (byte) 10; //'\n'

public SocketRequest(InputStream input) {
this.input = input;
}

public SocketRequest(Socket socket) {
this.input = socket.getInputStream();
}

public void ReadData() { //解析 獲得InputStream的數據

ReadHeader(); //頭部

if (beChucked) { //為Chucked
int ChuckSize = 0;
while ((ChuckSize = getChuckSize()) > 0) { //多個Chucked
readLenData(ChuckSize + 2); //讀取定長數據
}
readLenData(2); //最后的2位
}

if (CONTENT_LENGTH > 0) {
readLenData(CONTENT_LENGTH); //讀取定長數據
}

uri = ""; //parseUri(new String(request));
}

private void readLenData(int size) { //讀取定長數據
int readed = 0; //已經讀取數
try {
int available = 0; //input.available(); //可讀數
if (available > (size - readed)) {
available = size - readed;
} while (readed < size) {
while (available == 0) { //等到有數據可讀
available = input.available(); //可讀數
}
if (available > (size - readed)) {
available = size - readed; //size-readed--剩余數
}
if (available > 2048) {
available = 2048; //size-readed--剩余數
}
byte[] buffer = new byte[available];
int reading = input.read(buffer);
request = request.append(new String(buffer, 0, reading)); //byte數組相加
readed += reading; //已讀字符
}
} catch (IOException e) {
System.out.println("Read readLenData Error!");
}
}

private void ReadHeader() { //讀取頭部 并獲得大小
byte[] crlf = new byte[1];
int crlfNum = 0; //已經連接的回車換行數 crlfNum=4為頭部結束
try {
while (input.read(crlf) != -1) { //讀取頭部
if (crlf[0] == crlf13 || crlf[0] == crlf10) {
crlfNum++;
} else {
crlfNum = 0;
} //不是則清
request = request.append(new String(crlf, 0, 1)); //byte數組相加
if (crlfNum == 4) {
break;
}
}
} catch (IOException e) {
System.out.println("Read Http Header Error!");
return;
}

String tempStr = (new String(request)).toUpperCase();

//這里我只處理了GET與POST方法
String strMethod = tempStr.substring(0, 4);
if (strMethod.equals("GET ")) { //前
beGet = true;
} else if (strMethod.equals("POST")) {
bePost = true;
getContentlen_Chucked(tempStr);
} else {
System.out.println(" 不支持的HTTP包類型");

} //其它的其它類型 暫不支持
}

private void getContentlen_Chucked(String tempStr) { //獲得長度 CONTENT-LENGTH 或 是否為CHUNKED型
String ss1 = "CONTENT-LENGTH:";
String ss2 = new String("TRANSFER-ENCODING: CHUNKED");

int clIndex = tempStr.indexOf(ss1);
int chuckIndex = tempStr.indexOf(ss2); //為CHUNKED型
byte requst[] = tempStr.getBytes();
if (clIndex != -1) { //從clIndex+1起至\r\n
StringBuffer sb = new StringBuffer();

for (int i = (clIndex + 16); ; i++) {
if (requst[i] != (byte) 13 && requst[i] != (byte) 10) {
sb.append((char) requst[i]);
} else {
break;
}
}

CONTENT_LENGTH = Integer.parseInt(sb.toString()); //正式的HTML文件的大小
//System.out.println("CONTENT_LENGTH== "+CONTENT_LENGTH);
}
if (chuckIndex != -1) {
beChucked = true;
}
}

private int getChuckSize() { //Chuck大小
byte[] crlf = new byte[1];
StringBuffer sb1 = new StringBuffer();

int crlfNum = 0; //已經連接的回車換行數 crlfNum=4為頭部結束

try {
while (input.read(crlf) != -1) { //讀取頭部
if (crlf[0] == crlf13 || crlf[0] == crlf10) {
crlfNum++;
} else {
crlfNum = 0;
} //不是則清
sb1.append((char) crlf[0]);
request = request.append(new String(crlf, 0, 1)); //byte數組相加
if (crlfNum == 2) {
break;
}
}
} catch (IOException e) {
System.out.println("Read Http Package Error!");
return 0;
}

return Integer.parseInt((sb1.toString()).trim(), 16); //16進控制
}

//通過此來進行過濾,是否為發至目標服務器的HTTP包
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1) {
return requestString.substring(index1 + 1, index2);
}
}
return null;
}

public String getData() {
return request.toString();
}
}



使用此類:
SocketRequest request = new SocketRequest(socket); //socket為ServerSocket.accept()返回的Socket實例
request.ReadData(); //讀取數據
request.getData();

為什么我要用這么大的力量去讀取呢,尤其是在因為Socket連接在發送數據時,由于網絡的原因經常會發生延遲現象,可能在服務器端開始接收數據時可能只有部分數據可以從InputStream中獲得,在一些地方處理不當時,可能只能獲得不完整的數據或是錯誤的數據。

從 InputStream讀取字節時有多種辦法:
常用int read()與int read(byte[] b)。在用read(byte[])時,程序員經常會犯錯誤,因為在網絡環境中,讀取的數據量不一定等于參數的大小。

總結

以上是生活随笔為你收集整理的解读HTTP包的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 精品国内自产拍在线观看视频 | 欧美激情日韩 | 嫩草嫩草嫩草 | 日韩丰满少妇无码内射 | 一级特黄妇女高潮2 | av一区二区三区在线观看 | 日韩人妻无码精品综合区 | 国产aⅴ无码片毛片一级一区2 | 福利片网址 | 天天超碰| 亚洲精品久久久蜜桃网尤妮丝 | 亚洲视频一区二区三区在线观看 | 熟妇人妻无乱码中文字幕真矢织江 | 欧美激情成人在线 | 男女一区| 亚洲天堂免费视频 | 日韩在线观看视频网站 | av片观看| 黄色二级视频 | 色多多污污 | 日本午夜视频 | 久久亚洲AV成人无码国产野外 | 在线观看午夜视频 | 91久久久久国产一区二区 | 欧美激情一区二区视频 | 99精品一区 | 中国a级黄色片 | 欧美一区二区三区四区视频 | 射一射| 欧美一级一级 | a黄色大片 | x88av视频 | 自宅警备员在线观看 | 中文字幕不卡 | 亚洲视频欧美 | 一本免费视频 | 久久婷婷国产 | 久久久久久久久免费看无码 | 三级视频在线播放 | 特级淫片aaaaaaa级附近的 | 欧美日韩视频一区二区三区 | 播五月婷婷 | 久久久久久久色 | 一级特黄a大片免费 | 国产成人无码a区在线观看视频 | 天天国产视频 | 久久久久三级 | 亚洲欧美视频 | 久久久精品人妻av一区二区三区 | 可以免费观看av的网站 | 国产精品国产三级国产专播精品人 | 欧美一级片免费看 | 污版视频在线观看 | 亚洲婷婷小说 | av免费国产 | 国产在线视频网 | 色图在线观看 | 亚洲av无码一区二区三区性色 | 日韩视频精品在线 | 蜜桃成人免费视频 | 日韩性xxx| 欧美一级高清片 | 奇米影视播放器 | 亚洲色婷婷一区二区三区 | 一级黄色美女 | 欧美草逼网 | av拍拍拍| 美女bb视频| 91免费网址 | 亚洲天堂视频在线播放 | 九九热精品在线 | 国产传媒一级片 | 国产剧情久久久 | 中文av网站 | 在线看a网站 | 久久久久久欧美精品se一二三四 | 又黄又爽视频在线观看 | 8x8ⅹ成人永久免费视频 | 日韩欧美视频网站 | 国产经典一区二区 | 日韩欧美一区二区三区久久婷婷 | a∨鲁丝一区鲁丝二区鲁丝三区 | 亚洲制服一区 | 葵司有码中文字幕二三区 | 亚洲第一黄色网 | 名校风暴在线观看免费高清完整 | 97人妻精品一区二区免费 | 扒开jk护士狂揉免费 | 成人免费公开视频 | 麻豆国产精品777777在线 | 91成人免费观看 | 六月丁香激情综合 | 国产精品人人爽人人爽 | 久久r这里只有精品 | 亚洲美女在线观看 | 国产原创91 | 天堂在线观看免费视频 | 国产激情综合五月久久 | 乱色视频 |