導航:首頁 > 源碼編譯 > http協議的源碼

http協議的源碼

發布時間:2023-01-28 19:26:52

⑴ R爬蟲必備基礎—HTTP協議

HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )伺服器傳輸超文本到本地瀏覽器的傳送協議。HTTP是一個基於TCP/IP通信協議來傳遞數據(HTML 文件, 圖片文件, 查詢結果等)。

HTTP協議工作於客戶端-服務端架構上。瀏覽器作為HTTP客戶端通過URL向HTTP服務端即WEB伺服器發送所有請求。Web伺服器有:Apache伺服器,IIS伺服器(Internet Information Services)等。Web伺服器根據接收到的請求後,向客戶端發送響應信息。HTTP默認埠號為80,但是你也可以改為8080或者其他埠。

通信流程說明:

①用戶首先通過滑鼠/鍵盤點擊或手動輸入目標鏈接(即URL(Uniform Resource Locators),中文名稱:統一資源定位符),向HTTP客戶端(如常見的瀏覽器)傳達數據查詢需求。
②HTTP客戶端收到需求後,將收到的URL信息向DNS伺服器(域名系統)詢問鏈接對應的具體IP地址,DNS伺服器返回URL對應的IP地址。
③HTTP客戶端再拿著IP地址通過TCP協議(傳輸控制協議,Transmission Control Protocol)和IP協議(Internet Protocol)向HTTP伺服器發出數據請求,等待伺服器響應。
④HTTP伺服器將請求的相關信息返回給HTTP客戶端,由客戶端返回給客戶。
⑤重復上述步驟①~④,直到所有請求執行完畢。

URL格式: 如 scheme://hostname:port/path?querystring#fragment 。一個完整的URL共有6部分構成,但是並不是每個部分都一定要具備。參數解釋如下:

HTTP是基於客戶端/服務端(C/S)的架構模型,通過一個可靠的鏈接來交換信息,是一個無狀態的請求/響應協議。一個HTTP"客戶端"是一個應用程序(Web瀏覽器或其他任何客戶端),通過連接到伺服器達到向伺服器發送一個或多個HTTP的請求的目的。一個HTTP"伺服器"同樣也是一個應用程序(通常是一個Web服務,如Apache Web伺服器或IIS伺服器等),通過接收客戶端的請求並向客戶端發送HTTP響應數據。HTTP使用統一資源標識符(Uniform Resource Identifiers, URI)來傳輸數據和建立連接。一旦建立連接後,數據消息就通過類似Internet郵件所使用的格式[RFC5322]和多用途Internet郵件擴展(MIME)[RFC2045]來傳送。

客戶端發送一個HTTP請求到伺服器的請求消息包括以下格式:請求行(request line)、請求頭部(header)、空行和請求數據四個部分組成,下圖給出了請求報文的一般格式。

如:

HTTP響應也由四個部分組成,分別是:狀態行、消息報頭、空行和響應正文。

如:

根據 HTTP 標准,HTTP 請求可以使用多種請求方法。HTTP1.0 定義了三種請求方法:GET, POST 和 HEAD方法。HTTP1.1 新增了六種請求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

在請求模式中,最常用的請求方法是GET和POST方法,在爬蟲過程中至關重要。這兩個方法都是從伺服器請求一個資源,但是在正文的使用上有所不同。GET方法是網路請求最通用方法,可理解為直接請求。POST則有所區別,需要提交表單信息才能請求到信息,比如說拉勾網招聘首頁需要用戶輸入地點、薪資范圍等信息才能請求到匹配的網頁界面。

HTTP請求頭提供了關於請求,響應或者其他的發送實體的信息。下面將具體來介紹HTTP響應頭信息。

當瀏覽者訪問一個網頁時,瀏覽者的瀏覽器會向網頁所在伺服器發出請求。當瀏覽器接收並顯示網頁前,此網頁所在的伺服器會返回一個包含HTTP狀態碼的信息頭(server header)用以響應瀏覽器的請求。HTTP狀態碼的英文為HTTP Status Code。下面是常見的HTTP狀態碼:

200 - 請求成功
301 - 資源(網頁等)被永久轉移到其它URL
404 - 請求的資源(網頁等)不存在
500 - 內部伺服器錯誤

HTTP狀態碼由三個十進制數字組成,第一個十進制數字定義了狀態碼的類型,後兩個數字沒有分類的作用。HTTP狀態碼共分為5種類型:

Content-Type(內容類型),一般是指網頁中存在的 Content-Type,用於定義網路文件的類型和網頁的編碼,決定瀏覽器將以什麼形式、什麼編碼讀取這個文件,比如text/html/xml/json/jpg/gif/pdf等。Content-Type 標頭告訴客戶端實際返回的內容的內容類型,如下圖,返回的是html格式文件。

當我們在地址欄輸入 www.sina.com.cn 時,瀏覽器將顯示新浪的首頁。在這個過程中,瀏覽器都幹了哪些事情呢?通過Network的記錄,我們就可以知道。在Network中,定位到第一條記錄,點擊右側Headers,從Request Headers中可以看到瀏覽器發給新浪伺服器的請求:請求網址為 https://www.sina.com.cn/ ,請求方法為GET.

繼續往下找到Response Headers,它顯示伺服器返回的原始響應數據。其中200表示一個成功的響應,後面的OK是說明。失敗的響應有404 Not Found:網頁不存在,500 Internal Server Error:伺服器內部出錯,等等。Content-Type指示響應的內容,這里是text/html表示HTML網頁。請注意,瀏覽器就是依靠Content-Type來判斷響應的內容是網頁還是圖片,是視頻還是音樂。

HTTP響應返回的是HTML源碼,點擊Response,可以看到返回的html網頁源碼。

接著,當瀏覽器讀取到新浪首頁的HTML源碼後,它會解析HTML,顯示頁面,然後,根據HTML裡面的各種鏈接,再發送HTTP請求給新浪伺服器,拿到相應的圖片、視頻、Flash、javaScript腳本、CSS等各種資源,最終顯示出一個完整的頁面。

參考: https://www.runoob.com/http/http-tutorial.html

更多內容可關注公共號「YJY技能修煉」~~~

往期回顧
R爬蟲在工作中的一點妙用
R爬蟲必備基礎——HTML和CSS初識
R爬蟲必備基礎——靜態網頁+動態網頁
R爬蟲必備——rvest包的使用
R爬蟲必備基礎——CSS+SelectorGadget
R爬蟲必備基礎—Chrome開發者工具(F12)

⑵ 如何開發自己的HttpServer-NanoHttpd源碼解讀

1.能接受HttpRequest並返回HttpResponse
2.滿足一個Server的基本特徵,能夠長時間運行

關於Http協議一般HttpServer都會聲明支持Http協議的哪些特性,nanohttpd作為一個輕量級的httpserver只實現了最簡單、最常用的功能,不過我們依然可以從中學習很多。

首先看下NanoHttpd類的start函數

[java] view plain
public void start() throws IOException {
myServerSocket = new ServerSocket();
myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));

myThread = new Thread(new Runnable() {
@Override
public void run() {
do {
try {
final Socket finalAccept = myServerSocket.accept();
registerConnection(finalAccept);
finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
final InputStream inputStream = finalAccept.getInputStream();
asyncRunner.exec(new Runnable() {
@Override
public void run() {
OutputStream outputStream = null;
try {
outputStream = finalAccept.getOutputStream();
TempFileManager tempFileManager = tempFileManagerFactory.create();
HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
while (!finalAccept.isClosed()) {
session.execute();
}
} catch (Exception e) {
// When the socket is closed by the client, we throw our own SocketException
// to break the "keep alive" loop above.
if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
e.printStackTrace();
}
} finally {
safeClose(outputStream);
safeClose(inputStream);
safeClose(finalAccept);
unRegisterConnection(finalAccept);
}
}
});
} catch (IOException e) {
}
} while (!myServerSocket.isClosed());
}
});
myThread.setDaemon(true);
myThread.setName("NanoHttpd Main Listener");
myThread.start();
}
1.創建ServerSocket,bind制定埠

2.創建主線程,主線程負責和client建立連接
3.建立連接後會生成一個runnable對象放入asyncRunner中,asyncRunner.exec會創建一個線程來處理新生成的連接。
4.新線程首先創建了一個HttpSession,然後while(true)的執行httpSession.exec。
這里介紹下HttpSession的概念,HttpSession是java里Session概念的實現,簡單來說一個Session就是一次httpClient->httpServer的連接,當連接close後session就結束了,如果沒結束則session會一直存在。這點從這里的代碼也能看到:如果socket不close或者exec沒有拋出異常(異常有可能是client段斷開連接)session會一直執行exec方法。
一個HttpSession中存儲了一次網路連接中server應該保存的信息,比如:URI,METHOD,PARAMS,HEADERS,COOKIES等。
5.這里accept一個client的socket就創建一個獨立線程的server模型是ThreadServer模型,特點是一個connection就會創建一個thread,是比較簡單、常見的socket server實現。缺點是在同時處理大量連接時線程切換需要消耗大量的資源,如果有興趣可以了解更加高效的NIO實現方式。

當獲得client的socket後自然要開始處理client發送的httprequest。

Http Request Header的parse:

[plain] view plain
// Read the first 8192 bytes.
// The full header should fit in here.
// Apache's default header limit is 8KB.
// Do NOT assume that a single read will get the entire header at once!
byte[] buf = new byte[BUFSIZE];
splitbyte = 0;
rlen = 0;
{
int read = -1;
try {
read = inputStream.read(buf, 0, BUFSIZE);
} catch (Exception e) {
safeClose(inputStream);
safeClose(outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
if (read == -1) {
// socket was been closed
safeClose(inputStream);
safeClose(outputStream);
throw new SocketException("NanoHttpd Shutdown");
}
while (read > 0) {
rlen += read;
splitbyte = findHeaderEnd(buf, rlen);
if (splitbyte > 0)
break;
read = inputStream.read(buf, rlen, BUFSIZE - rlen);
}
}
1.讀取socket數據流的前8192個位元組,因為http協議中頭部最長為8192

2.通過findHeaderEnd函數找到header數據的截止位置,並把位置保存到splitbyte內。

[java] view plain
if (splitbyte < rlen) {
inputStream.unread(buf, splitbyte, rlen - splitbyte);
}

parms = new HashMap<String, String>();
if(null == headers) {
headers = new HashMap<String, String>();

}
1.http協議規定header和body之間使用兩個回車換行分割

1.Http協議第一行是Method URI HTTP_VERSION

2.後面每行都是KEY:VALUE格式的header
3.uri需要經過URIDecode處理後才能使用
4.uri中如果包含?則表示有param,httprequest的param一般表現為:/index.jsp?username=xiaoming&id=2

下面是處理cookie,不過這里cookie的實現較為簡單,所以跳過。之後是serve方法,serve方法提供了用戶自己實現httpserver具體邏輯的很好介面。在NanoHttpd中的serve方法實現了一個默認的簡單處理功能。

[java] view plain

發送response的步驟如下:

1.設置mimeType和Time等內容。
2.創建一個PrintWriter,按照HTTP協議依次開始寫入內容
3.第一行是HTTP的返回碼
4.然後是content-Type
5.然後是Date時間
6.之後是其他的HTTP Header
7.設置Keep-Alive的Header,Keep-Alive是Http1.1的新特性,作用是讓客戶端和伺服器端之間保持一個長鏈接。
8.如果客戶端指定了ChunkedEncoding則分塊發送response,Chunked Encoding是Http1.1的又一新特性。一般在response的body比較大的時候使用,server端會首先發送response的HEADER,然後分塊發送response的body,每個分塊都由chunk length\r\n和chunk data\r\n組成,最後由一個0\r\n結束。

9.如果沒指定ChunkedEncoding則需要指定Content-Length來讓客戶端指定response的body的size,然後再一直寫body直到寫完為止。

⑶ OkHttp源碼解析 (三)——代理和路由

初看OkHttp源碼,由於對Address、Route、Proxy、ProxySelector、RouteSelector等理解不夠,讀源碼非常吃力,看了幾遍依然對於尋找復用連接、創建連接、連接伺服器、連接代理伺服器、創建隧道連接等邏輯似懂非懂,本篇決定梳理一遍相關的概念及基本原理。

● HTTP/1.1(HTTPS)
● HTTP/2
● SPDY

一個http請求的流程(直連):
1、輸入url及參數;
2、如果是url是域名則解析ip地址,可能對應多個ip,如果沒有指定埠,則用默認埠,http請求用80;
3、創建socket,根據ip和埠連接伺服器(socket內部會完成3次TCP握手);
4、socket成功連接後,發送http報文數據。

一個https請求的流程(直連):
1、輸入url及參數;
2、如果是url是域名則解析ip地址,可能對應多個ip,如果沒有指定埠,則用默認埠,https請求用443;
3、創建socket,根據ip和埠連接伺服器(socket內部會完成3次TCP握手);
4、socket成功連接後進行TLS握手,可通過java標准款提供的SSLSocket完成;
5、握手成功後,發送https報文數據。

1、分類
● HTTP代理:普通代理、隧道代理
● SOCKS代理:SOCKS4、SOCKS5

2、HTTP代理分類及說明
普通代理
HTTP/1.1 協議的第一部分。其代理過程為:
● client 請求 proxy
● proxy 解析請求獲取 origin server 地址
● proxy 向 origin server 轉發請求
● proxy 接收 origin server 的響應
● proxy 向 client 轉發響應
其中proxy獲取目的伺服器地址的標准方法是解析 request line 里的 request-URL。因為proxy需要解析報文,因此普通代理無法適用於https,因為報文都是加密的。

隧道代理
通過 Web 代理伺服器用隧道方式傳輸基於 TCP 的協議。
請求包括兩個階段,一是連接(隧道)建立階段,二是數據通信(請求響應)階段,數據通信是基於 TCP packet ,代理伺服器不會對請求及響應的報文作任何的處理,都是原封不動的轉發,因此可以代理 HTTPS請求和響應。
代理過程為:
● client 向 proxy 發送 CONNET 請求(包含了 origin server 的地址)
● proxy 與 origin server 建立 TCP 連接
● proxy 向 client 發送響應
● client 向 proxy 發送請求,proxy 原封不動向 origin server 轉發請求,請求數據不做任何封裝,為原生 TCP packet.

3、SOCKS代理分類及說明
● SOCKS4:只支持TCP協議(即傳輸控制協議)
● SOCKS5: 既支持TCP協議又支持UDP協議(即用戶數據包協議),還支持各種身份驗證機制、伺服器端域名解析等。
SOCK4能做到的SOCKS5都可得到,但反過來卻不行,比如我們常用的聊天工具QQ在使用代理時就要求用SOCKS5代理,因為它需要使用UDP協議來傳輸數據。

有了上面的基礎知識,下面分析結合源碼分析OkHttp路由相關的邏輯。OkHttp用Address來描述與目標伺服器建立連接的配置信息,但請求輸入的可能是域名,一個域名可能對於多個ip,真正建立連接是其中一個ip,另外,如果設置了代理,客戶端是與代理伺服器建立直接連接,而不是目標伺服器,代理又可能是域名,可能對應多個ip。因此,這里用Route來描述最終選擇的路由,即客戶端與哪個ip建立連接,是代理還是直連。下面對比下Address及Route的屬性,及路由選擇器RouteSelector。

描述與目標伺服器建立連接所需要的配置信息,包括目標主機名、埠、dns,SocketFactory,如果是https請求,包括TLS相關的SSLSocketFactory 、HostnameVerifier 、CertificatePinner,代理伺服器信息Proxy 、ProxySelector 。

Route提供了真正連接伺服器所需要的動態信息,明確需要連接的伺服器IP地址及代理伺服器,一個Address可能會有很多個路由Route供選擇(一個DNS對應對個IP)。

Address和Route都是數據對象,沒有提供操作方法,OkHttp另外定義了RouteSelector來完成選擇的路由的操作。

1、讀取代理配置信息:resetNextProxy()

讀取代理配置:
● 如果有指定代理(不讀取系統配置,在OkHttpClient實例中指定),則只用1個該指定代理;
● 如果沒有指定,則讀取系統配置的,可能有多個。

2、獲取需要嘗試的socket地址(目標伺服器或者代理伺服器):resetNextInetSocketAddress()

結合Address的host和代理,解析要嘗試的套接字地址(ip+埠)列表:
● 直連或者SOCK代理, 則用目標伺服器的主機名和埠,如果是HTTP代理,則用代理伺服器的主機名和埠;
● 如果是SOCK代理,根據目標伺服器主機名和埠號創建未解析的套接字地址,列表只有1個地址;
● 如果是直連或HTTP代理,先DNS解析,得到InetAddress列表(沒有埠),再創建InetSocketAddress列表(帶上埠),InetSocketAddress與InetAddress的區別是前者帶埠信息。

3、獲取路由列表:next()

選擇路由的流程解析:
● 遍歷每個代理對象,可能多個,直連的代理對象為Proxy.DIRECT(實際是沒有中間代理的);
● 對每個代理獲取套接字地址列表;
● 遍歷地址列表,創建Route,判斷Route如果在路由黑名單中,則添加到失敗路由列表,不在黑名單中則添加到待返回的Route列表;
● 如果最後待返回的Route列表為空,即可能所有路由都在黑名單中,實在沒有新路由了,則將失敗的路由集合返回;
● 傳入Route列表創建Selection對象,對象比較簡單,就是一個目標路由集合,及讀取方法。

為了避免不必要的嘗試,OkHttp會把連接失敗的路由加入到黑名單中,由RouteDatabase管理,該類比較簡單,就是一個失敗路由集合。

1、創建Address
Address的創建在RetryAndFollowUpInteceptor里,每次請求會聲明一個新的Address及StreamAllocation對象,而StreamAllocation使用Address創建RouteSelector對象,在連接時RouteSelector確定請求的路由。

每個Requst都會構造一個Address對象,構造好了Address對象只是有了與伺服器連接的配置信息,但沒有確定最終伺服器的ip,也沒有確定連接的路由。

2、創建RouteSelector
在StreamAllocation聲明的同時會聲明路由選擇器RouteSelector,為一次請求尋找路由。

3、選擇可用的路由Route

下面在測試過程跟蹤實例對象來理解,分別測試直連和HTTP代理HTTP2請求路由的選擇過程:
● 直連請求流程
● HTTP代理HTTPS流程
請求url: https://www.jianshu.com/p/63ba15d8877a

1、構造address對象

2、讀取代理配置:resetNextProxy

3、解析目標伺服器套接字地址:resetNextInetSocketAddress

4、選擇Route創建RealConnection

5、確定協議

測試方法:
● 在PC端打開Charles,設置埠,如何設置代理,網上有教程,比較簡單;
● 手機打開WIFI,選擇連接的WIFI修改網路,在高級選項中設置中指定了代理伺服器,ip為PC的ip,埠是Charles剛設置的埠;
● OkHttpClient不指定代理,發起請求。

1、構造address對象

2、讀取代理配置:resetNextProxy

3、解析目標伺服器套接字地址:resetNextInetSocketAddress

4、選擇Route創建RealConnection

5、創建隧道
由於是代理https請求,需要用到隧道代理。

從圖可以看出,建立隧道其實是發送CONNECT請求,header包括欄位Proxy-Connection,目標主機名,請求內容類似:

6、確定協議,SSL握手

1、代理可分為HTTP代理和SOCK代理;
2、HTTP代理又分為普通代理和隧道代理;普通代理適合明文傳輸,即http請求;隧道代理僅轉發TCP包,適合加密傳輸,即https/http2;
3、SOCK代理又分為SOCK4和SOCK5,區別是後者支持UDP傳輸,適合代理聊天工具如QQ;
4、沒有設置代理(OkHttpClient沒有指定同時系統也沒有設置),客戶端直接與目標伺服器建立TCP連接;
5、設置了代理,代理http請求時,客戶端與代理伺服器建立TCP連接,如果代理伺服器是域名,則解釋代理伺服器域名,而目標伺服器的域名由代理伺服器解析;
6、設置了代理,代理https/http2請求時,客戶端與代理伺服器建立TCP連接,發送CONNECT請求與代理伺服器建立隧道,並進行SSL握手,代理伺服器不解析數據,僅轉發TCP數據包。

如何正確使用 HTTP proxy
OkHttp3中的代理與路由
HTTP 代理原理及實現(一)

⑷ 一套優質直播軟體源碼中的常用傳輸協議有哪些

常用的傳輸協議RTMP,HLS ,HTTP-FLV,HLS和HTTP-FLV都是基於HTTP的協議,RTMP是基於TCP。
HLS即Http Live Streaming,是由蘋果提出基於HTTP的流媒體傳輸協議。它有一個非常大的優點就是HTML5可以直接打開播放;這個意味著可以把一個直播鏈接通過微信等轉發分享,不需要安裝任何獨立的APP,有瀏覽器即可,很通用所以流行度很高。但是它有一個很大的弱點就是延遲高。
HTTP-FLV是一種將直播流模擬成FLV文件,通過HTTP協議進行下載的模式來實現流媒體傳輸的協議。
RTMP即Real Time Messaging Protocol,是 Adobe Systems 公司為 Flash 播放器和伺服器之間音頻、視頻和數據傳輸開發的開放協議。協議基於 TCP,是一個協議族,包括 RTMP 基本協議及 RTMPT/RTMPS/RTMPE 等多種變種。

⑸ 遠程訪問用的是http協議,下面是源代碼

結論:①string URi = "http://192.168.0.105//www/";
URi的值應該是一個URL地址吧,怎麼105後面有兩個/
②你這樣的訪問方式涉及到客戶端對伺服器端的訪問許可權問題,我沒試過以這樣的方式將文件上傳到伺服器的,而且我覺得應該不是這樣的方式。
解決方案建議:
①可以在伺服器端開通ftp功能,將文件發送到ftp里,代碼如下:
privatevoidbutton1_Click(objectsender,EventArgse)

{

WebClientw=newWebClient();

w.Credentials=newNetworkCredential("sa","sa");//登陸ftp的用戶名密碼

w.UploadFile("ftp://221.224.78.82/skdb/up.xls",@"e:\1.xls");////前面是遠程ftp文件夾路徑後面是:本地上傳的文件路徑

w.Dispose();

MessageBox.Show("上傳成功!");

}

②伺服器端有IIS的話通過WebService的方式實現文件上傳
這個方法相對比較復雜,建議用第一種,如果要採用這種的話到時我再發一個例子給你⊙﹏⊙

⑹ swoole源碼-http請求數據接收

swoole的協議解析是在reactor線程中處理的,在reactor swReactorThread_loop() 事件循環中通過 swPort_set_protocol() 注冊了協議解析處理函數, 其中http的協議解析函數是 swPort_onRead_http()

reactor線程啟動文件: src/network/ReactorThread.c swReactorThread_start()

如果對reactor事件處理流程不清楚可以參考這篇文章 swoole-主線程和reactor線程交互流程

下面主要來看 swPort_onRead_http, 對http協議的解析 。 這里說明下,有了這個協議解析後,如果你啟動的是http server或者,啟動是配置了 open_http_protocol 選項, 都會執行swPort_onRead_http對數據進行http協議解析, 這樣在 onReceive() 中接收到的就是整個http請求的數據

swPort_onRead_http() 函數在 src/network/Port.c 文件中, 源碼就不帖了, 說下這個函數的主要功能

通過swReactorThread_dispatch後 會回調worker的 onRequest,或者onReceive() 的回調函數, 關於http協議的解析,以及數據是如何解析到 Http\Request->header、 Http\Request->get、 Http\Request->post等是在 swoole_http_server.c 中完成

⑺ http協議解析 請求行的信息怎麼提取 c語言源碼

實現步驟:
1)用Wireshark軟體抓包得到test.pcap文件
2)程序:分析pcap文件頭 -> 分析pcap_pkt頭 -> 分析幀頭 -> 分析ip頭 -> 分析tcp頭 -> 分析http信息
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<time.h>
#define BUFSIZE 10240
#define STRSIZE 1024
typedef long bpf_int32;
typedef unsigned long bpf_u_int32;
typedef unsigned short u_short;
typedef unsigned long u_int32;
typedef unsigned short u_int16;
typedef unsigned char u_int8;
//pacp文件頭結構體
struct pcap_file_header
{
bpf_u_int32 magic; /* 0xa1b2c3d4 */
u_short version_major; /* magjor Version 2 */
u_short version_minor; /* magjor Version 4 */
bpf_int32 thiszone; /* gmt to local correction */
bpf_u_int32 sigfigs; /* accuracy of timestamps */
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
};
//時間戳
struct time_val
{
long tv_sec; /* seconds 含義同 time_t 對象的值 */
long tv_usec; /* and microseconds */
};
//pcap數據包頭結構體
struct pcap_pkthdr
{
struct time_val ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
//數據幀頭
typedef struct FramHeader_t
{ //Pcap捕獲的數據幀頭
u_int8 DstMAC[6]; //目的MAC地址
u_int8 SrcMAC[6]; //源MAC地址
u_short FrameType; //幀類型
} FramHeader_t;
//IP數據報頭
typedef struct IPHeader_t
{ //IP數據報頭
u_int8 Ver_HLen; //版本+報頭長度
u_int8 TOS; //服務類型
u_int16 TotalLen; //總長度
u_int16 ID; //標識
u_int16 Flag_Segment; //標志+片偏移
u_int8 TTL; //生存周期
u_int8 Protocol; //協議類型
u_int16 Checksum; //頭部校驗和
u_int32 SrcIP; //源IP地址
u_int32 DstIP; //目的IP地址
} IPHeader_t;
//TCP數據報頭
typedef struct TCPHeader_t
{ //TCP數據報頭
u_int16 SrcPort; //源埠
u_int16 DstPort; //目的埠
u_int32 SeqNO; //序號
u_int32 AckNO; //確認號
u_int8 HeaderLen; //數據報頭的長度(4 bit) + 保留(4 bit)
u_int8 Flags; //標識TCP不同的控制消息
u_int16 Window; //窗口大小
u_int16 Checksum; //校驗和
u_int16 UrgentPointer; //緊急指針
}TCPHeader_t;
//
void match_http(FILE *fp, char *head_str, char *tail_str, char *buf, int total_len); //查找 http 信息函數
//
int main()
{
struct pcap_file_header *file_header;
struct pcap_pkthdr *ptk_header;
IPHeader_t *ip_header;
TCPHeader_t *tcp_header;
FILE *fp, *output;
int pkt_offset, i=0;
int ip_len, http_len, ip_proto;
int src_port, dst_port, tcp_flags;
char buf[BUFSIZE], my_time[STRSIZE];
char src_ip[STRSIZE], dst_ip[STRSIZE];
char host[STRSIZE], uri[BUFSIZE];
//初始化
file_header = (struct pcap_file_header *)malloc(sizeof(struct pcap_file_header));
ptk_header = (struct pcap_pkthdr *)malloc(sizeof(struct pcap_pkthdr));
ip_header = (IPHeader_t *)malloc(sizeof(IPHeader_t));
tcp_header = (TCPHeader_t *)malloc(sizeof(TCPHeader_t));
memset(buf, 0, sizeof(buf));
//
if((fp = fopen(「test.pcap」,」r」)) == NULL)
{
printf(「error: can not open pcap file\n」);
exit(0);
}
if((output = fopen(「output.txt」,」w+」)) == NULL)
{
printf(「error: can not open output file\n」);
exit(0);
}
//開始讀數據包
pkt_offset = 24; //pcap文件頭結構 24個位元組
while(fseek(fp, pkt_offset, SEEK_SET) == 0) //遍歷數據包
{
i++;
//pcap_pkt_header 16 byte
if(fread(ptk_header, 16, 1, fp) != 1) //讀pcap數據包頭結構
{
printf(「\nread end of pcap file\n」);
break;
}
pkt_offset += 16 + ptk_header->caplen; //下一個數據包的偏移值
strftime(my_time, sizeof(my_time), 「%Y-%m-%d %T」, localtime(&(ptk_header->ts.tv_sec))); //獲取時間
// printf(「%d: %s\n」, i, my_time);
//數據幀頭 14位元組
fseek(fp, 14, SEEK_CUR); //忽略數據幀頭
//IP數據報頭 20位元組
if(fread(ip_header, sizeof(IPHeader_t), 1, fp) != 1)
{
printf(「%d: can not read ip_header\n」, i);
break;
}
inet_ntop(AF_INET, (void *)&(ip_header->SrcIP), src_ip, 16);
inet_ntop(AF_INET, (void *)&(ip_header->DstIP), dst_ip, 16);
ip_proto = ip_header->Protocol;
ip_len = ip_header->TotalLen; //IP數據報總長度
// printf(「%d: src=%s\n」, i, src_ip);
if(ip_proto != 0×06) //判斷是否是 TCP 協議
{
continue;
}
//TCP頭 20位元組
if(fread(tcp_header, sizeof(TCPHeader_t), 1, fp) != 1)
{
printf(「%d: can not read ip_header\n」, i);
break;
}
src_port = ntohs(tcp_header->SrcPort);
dst_port = ntohs(tcp_header->DstPort);
tcp_flags = tcp_header->Flags;
// printf(「%d: src=%x\n」, i, tcp_flags);
if(tcp_flags == 0×18) // (PSH, ACK) 3路握手成功後
{
if(dst_port == 80) // HTTP GET請求
{
http_len = ip_len – 40; //http 報文長度
match_http(fp, 「Host: 「, 「\r\n」, host, http_len); //查找 host 值
match_http(fp, 「GET 「, 「HTTP」, uri, http_len); //查找 uri 值
sprintf(buf, 「%d: %s src=%s:%d dst=%s:%d %s%s\r\n」, i, my_time, src_ip, src_port, dst_ip, dst_port, host, uri);
//printf(「%s」, buf);
if(fwrite(buf, strlen(buf), 1, output) != 1)
{
printf(「output file can not write」);
break;
}
}
}
} // end while
fclose(fp);
fclose(output);
return 0;
}
//查找 HTTP 信息
void match_http(FILE *fp, char *head_str, char *tail_str, char *buf, int total_len)
{
int i;
int http_offset;
int head_len, tail_len, val_len;
char head_tmp[STRSIZE], tail_tmp[STRSIZE];
//初始化
memset(head_tmp, 0, sizeof(head_tmp));
memset(tail_tmp, 0, sizeof(tail_tmp));
head_len = strlen(head_str);
tail_len = strlen(tail_str);
//查找 head_str
http_offset = ftell(fp); //記錄下HTTP報文初始文件偏移
while((head_tmp[0] = fgetc(fp)) != EOF) //逐個位元組遍歷
{
if((ftell(fp) – http_offset) > total_len) //遍歷完成
{
sprintf(buf, 「can not find %s \r\n」, head_str);
exit(0);
}
if(head_tmp[0] == *head_str) //匹配到第一個字元
{
for(i=1; i<head_len; i++) //匹配 head_str 的其他字元
{
head_tmp[i]=fgetc(fp);
if(head_tmp[i] != *(head_str+i))
break;
}
if(i == head_len) //匹配 head_str 成功,停止遍歷
break;
}
}
// printf(「head_tmp=%s \n」, head_tmp);
//查找 tail_str
val_len = 0;
while((tail_tmp[0] = fgetc(fp)) != EOF) //遍歷
{
if((ftell(fp) – http_offset) > total_len) //遍歷完成
{
sprintf(buf, 「can not find %s \r\n」, tail_str);
exit(0);
}
buf[val_len++] = tail_tmp[0]; //用buf 存儲 value 直到查找到 tail_str
if(tail_tmp[0] == *tail_str) //匹配到第一個字元
{
for(i=1; i<tail_len; i++) //匹配 head_str 的其他字元
{
tail_tmp[i]=fgetc(fp);
if(tail_tmp[i] != *(tail_str+i))
break;
}
if(i == tail_len) //匹配 head_str 成功,停止遍歷
{
buf[val_len-1] = 0; //清除多餘的一個字元
break;
}
}
}
// printf(「val=%s\n」, buf);
fseek(fp, http_offset, SEEK_SET); //將文件指針 回到初始偏移
}

閱讀全文

與http協議的源碼相關的資料

熱點內容
用紙做解壓玩具不用澆水 瀏覽:582
谷輪壓縮機序列號 瀏覽:734
牛頓插值法編程 瀏覽:364
php多用戶留言系統 瀏覽:727
安卓和蘋果如何切換流量 瀏覽:703
怎麼知道dns伺服器是多少 瀏覽:976
5995用什麼簡便演算法脫式計算 瀏覽:918
電腦上如何上小米雲伺服器地址 瀏覽:921
手機資料解壓密碼 瀏覽:444
44引腳貼片單片機有哪些 瀏覽:692
阿里程序員腦圖 瀏覽:189
廣東編程貓學習班 瀏覽:708
上海數控編程培訓學校 瀏覽:313
怎麼下載我的解壓神器 瀏覽:634
lib文件無用代碼會編譯嗎 瀏覽:28
我的世界嗨皮咳嗽伺服器怎麼下 瀏覽:1002
mvn命令順序 瀏覽:978
車貸還完多少時間解壓 瀏覽:964
java頁面開發 瀏覽:820
學編程的小發明 瀏覽:25