那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

curl工具的簡易實現

FPGA之家 ? 來源:FPGA之家 ? 作者:FPGA之家 ? 2022-08-24 09:05 ? 次閱讀

前言

一般來說,使用套接字進行網絡編程時,默認使用linux內核提供的網絡服務。但是,現在我們自己在用戶空間構建了一個tcp協議棧,并且讓它為其他應用程序提供網絡服務,這勢必要求我們自己實現一套新的套接字接口,并且提供給其他應用程序指定使用。

但是,我們并不希望把該tcp協議棧封裝成動態庫的形式,因為這樣一來,應用程序的編譯是必須要把庫一起連接進去的。那么原生網絡編程開發的程序就不能基于我們tcp協議棧來運行了。

一種較好的設計思路是,把tcp協議棧剝離出來作為一個獨立的組件來運行,然后通過一個中間件,把網絡程序與tcp協議棧協同工作起來。這個中間件的主要工作就是負責偷龍換鳳,也就是把網絡程序中的內核網絡服務轉換成獨立運行的tcp協議棧的網絡服務。

核心思路:網絡程序(curl)+自定義套接字庫(liblevel.so)+tcp協議棧(level-ip),如下圖:

7365045c-2341-11ed-ba43-dac502259ad0.png

curl小工具

curl是一種命令行工具,作用是發出網絡請求,然后得到和提取數據,顯示在"標準輸出"(stdout)上面。我們直接在curl命令后加上網址和端口,就可以看到網頁源碼。比如抓取www.sina.com網址:

curl www.sina.com 80

下面我們來看一個curl工具的簡易實現,如下圖:

737ffb72-2341-11ed-ba43-dac502259ad0.png

第3行:判斷目標主機名是否合法

第11行:判斷目標端口好是否合法

第16行:完成主機名到地址解析

第21行:使用socket申請一個套接字描述符

第23行:使用connect函數發起tcp連接

第30行:按照http 1.1協議來填充要發送的內容,此處為http協議的get請求

第33行:調用write來發送網絡數據

第41行:在while循環中重復接收服務器返回來的網頁數據,并且打印在當前控制臺終端上。

這是標準的網絡應用程序,使用gcc命令編譯后即可運行。

gcc curl.c -o curl

liblevelip.so庫

level-ip腳本

liblevelip.so庫重新封裝了常用的socket套接字,并借助socket原生的本地套接字接口來與tcp協議棧(level-ip)進行數據通信。以后在curl程序使用socket套接字時,優先使用該庫的服務接口,而不是內核的網絡服務。這是通過level-ip這個shell腳本完成的,具體命令如下:

./level-ip curl www.sina.com 80

我們來分析一下level-ip這個shell腳本的原理,如下圖:

73a4d4a6-2341-11ed-ba43-dac502259ad0.png

第1行:執行該shell腳本由/bin/sh程序來執行。

第3行:指定腳本如果發生錯誤,或者遇到不存在的變量就報錯,并停止執行。

第5行:保存腳本的第一個參數到prog變量中。

第6行:去掉一個參數,即原來的1,2,依此類推。

第8行:LD_PRELOAD是一個環境變量,其指定的動態庫加載等級最高。@表示第二個參數之后的全部參數。

綜上所述,我們就可以確定curl程序是優先加載liblevelip.so庫來使用了,通過這種打樁技術,我們可以在加載階段替換部分系統函數的調用,比如我們常用的socket接口。

liblevelip.c文件

liblevelip.so庫由liblevellip.c文件編譯而來,該文件在tools文件夾中,我們逐步來分析一下這個c文件。

__libc_start_main函數

首先是__libc_start_main函數,該函數原本是glibc庫里面的函數,curl程序里面的main函數就是從這里開始被調用。但是我們在liblevelip.c里面實現了這個函數,并且liblevelip.so的庫加載順序優先于glibc的動態庫加載。因此在執行curl程序中的main函數之前,此函數先被執行。如下圖:

73af364e-2341-11ed-ba43-dac502259ad0.png

第3行:dlsym函數里的第一個參數為RTLD_NEXT,這意味著我們將從其他動態庫去加載__libc_start_main函數符號(比如glibc庫),然后把函數句柄賦值給__start_main變量。

第7~22行:從glibc庫中加載一部分linux系統原生提供的系統調用接口。因為我們的網絡服務還是要依賴于一些更底層的系統調用接口的。

第24行:初始化一個鏈表節點lvlip_socks。

第26行:調用glibc的原生__libc_start_main接口。

socket函數

接下來就是liblevelip.so對外提供的第一個網絡編程接口--socket函數了。該函數實現如下:

73ce1b9a-2341-11ed-ba43-dac502259ad0.png

第3~5行:檢查網絡通信協議族是否為tcp協議,如果不是tcp協議,則調用內核提供的網絡服務。

第9行:借助tcp本地套接字接口,與tcp協議棧建立連接,用于通信的本地文件為/tmp/lvlip.socket

第11行:申請一個lvlip_sock類型的buff用于管理socket信息。結構體類型如下:

struct lvlip_sock {    struct list_head list;    int lvlfd; /* For Level-IP IPC */    int fd;};

list成員變量為鏈表結點

lvlfd記錄與tcp協議棧通信的網絡文件描述符

fd記錄tcp協議棧的返回狀態發送socket消息給tcp協議棧第12行:記錄與tcp協議棧通信的網絡文件描述符到sock->lvlfd第13行:把這次的網絡通信消息加入lvlip_socks鏈表中第14行:網絡通信消息數量加1第16行:獲取當前線程的pid號

第17~18行:申請ipc_msg+ipc_socket結構體長度的buff,用于發送詳細的socket信息到tcp協議棧。結構體定義如下:

struct ipc_msg {    uint16_t type;    pid_t pid;    uint8_t data[];} __attribute__((packed));

type:記錄此次socket信息的具體類型

pid:記錄請求網絡服務的進程pid號

data:存放具體的通信內容

struct ipc_socket {    int domain;    int type;    int protocol;} __attribute__((packed));

實際上就是soket函數的三個參數。

第23~29行:把ipc_socket作為通信的具體內容填充到ipc_msg的data區域中去

第31行:調用transmit_lvlip()函數真正給tcp協議棧發送消息,并且等待協議棧的數據回復。

此處我們就把liblevelip.so中的socket函數給剖析清楚了,其他諸如close、connect、write、read、send、sendto、recv、此處我們就把liblevelip.so中的socket函數給剖析清楚了,其他諸如close、connect、write、read、send、sendto、recv、此處我們就把liblevelip.so中的socket函數給剖析清楚了,其他諸如close、connect、write、read、send、sendto、recv、recvfrom、poll、select等函數,原理都是一樣的,此處不再展開分析。

tcp協議棧(level-ip)

用戶空間的level-ip協議棧,在運行之初,就已經在main函數里面創建了一系列線程。如下圖:

73f2ec5e-2341-11ed-ba43-dac502259ad0.png

其中第9行,在run_threads()函數里創建了一系列線程,如下圖:

740ccf8e-2341-11ed-ba43-dac502259ad0.png

在這里,我們重點關注第5行創建的start_ipc_listener線程

該線程的實現如下:

74186d30-2341-11ed-ba43-dac502259ad0.png

第5行:指定tcp本地通信的路徑文件為"/tmp/lvlip.socket",與我們前面liblevelip.so庫的本地通信文件一致,這就說明它們之間確實是通過tco本地通信接口來通信的

第10行:調用socket接口開始進行tcp本地通信

第24行:調用bind函數綁定本地通信路徑

第31行:調用listen函數監聽指定端口,等待liblevelip.so庫發起連接

第46行:如果liblevelip.so庫發起連接,則調用accept函數準備開始收發信息。

第54行:每監聽到一個新的連接,新創建一個socket_ipc_open函數來進行數據的具體收發。

socket_ipc_open函數主要是負責通信信息的讀取,然后根據通信消息的類型不同,來進一步調用具體的處理函數,其實現如下:

743b7f0a-2341-11ed-ba43-dac502259ad0.png

第7行:調用read函數進行數據的讀取

第8行:調用具體指令的回調信息

demux_ipc_socket_call的函數非常簡單,實現如下:

74482b88-2341-11ed-ba43-dac502259ad0.png

前面我們在liblevelip.so庫中調用socket()函數的時候,發送的消息類型為IPC_SOCKET,所以在此處我們進一步分析ipc_socket()這個函數。

它的具體實現如下:

7455531c-2341-11ed-ba43-dac502259ad0.png

我們重點是關注第7行的_socket函數,該函數就是tcp協議棧的核心接口之一了,它是整個tcp協議棧的真正入口,我們以后再來專門分析這個接口。然后第9行,ipc_write函數負責把tcp協議棧的處理結果返回給liblevelip.so庫,代碼較為簡單,此處不再分析。

審核編輯:彭靜
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Curl
    +關注

    關注

    0

    文章

    17

    瀏覽量

    8204
  • 編譯
    +關注

    關注

    0

    文章

    661

    瀏覽量

    33040
  • 網絡程序
    +關注

    關注

    0

    文章

    3

    瀏覽量

    918

原文標題:Linux系統中間件的巧妙實現--以用戶空間的tcp協議棧為例

文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Tiny4412下CURL安裝與使用

    CURL是一個利用URL語法在命令行下工作的文件傳輸工具,1997年首次發行。它支持文件上傳和下載,所以是綜合傳輸工具,但按傳統,習慣稱CURL為下載
    的頭像 發表于 05-31 17:20 ?1575次閱讀
    Tiny4412下<b class='flag-5'>CURL</b>安裝與使用

    使用curl+wget抓取網頁方法

    打包下載地址:Windows平臺下的wget和curl工具包Windows平臺下,curl下載解壓后,直接是curl.exe格式,拷貝到系統命令目錄下 C:\Windows\Syst
    發表于 02-25 09:54

    操作步驟:添加i.MX6UL對curl軟件

    /libcurl目錄下),指令執行后會配置編譯器: 5. 使用交叉編譯工具鏈編譯: 6. 執行如下命令編譯安裝到步驟3中指定的位置。注意:本步驟會有多出錯誤提示,忽略即可。 7. 打開步驟3中的位置,此時目錄
    發表于 04-16 15:31

    【ELF 1開發板試用】5.移植 curl 工具進行網絡調試

    【ELF 1開發板試用】5.移植 curl 工具進行網絡調試 連接互聯網 動態 首先將開發板用網線與路由器LAN口相連接,可以執行動態獲取 IP ,執行 udhcpc -i eth0 這個命令會將
    發表于 11-28 19:39

    Curl 下載v7.17.1

    Curl支持協定眾多的文件傳輸程序,可下載FTP,HTT,HTTP,GOPHE,TELNET,DICT,FILE,LDAP的文件,還可以上傳HTTP POST,HTTP PUT,FTP uploading,HTTP form based upload,proxies,cookies 點擊查看:
    發表于 12-04 13:09 ?12次下載

    curl文件下載工具的使用方法

    curl文件下載工具的使用方法 curl是一個利用URL語法在命令行方
    發表于 12-04 13:03 ?1775次閱讀

    淺談cURL 和wget相似之處及優勢對比

    cURL與wget:你應該選用哪一個? wget 和 cURL 都可以下載內容,它們都被設計成可腳本化,都可以寫進腳本中,但是wget 簡單直接,可享受超凡的下載速度,cURL是一個多功能工具
    的頭像 發表于 02-13 13:04 ?4535次閱讀

    simple curl操作類

    ./oschina_soft/curl.zip
    發表于 05-30 11:02 ?1次下載
    simple <b class='flag-5'>curl</b>操作類

    cURL的使用方法

    curl 是常用的開源命令行工具,用來請求 Web 服務器。它的名字就是客戶端(client)的 URL 工具的意思。它的功能非常強大,命令行參數多達幾十種。它支持包括 FTP、HTTP、HTTPS、FTP、SCP,SFTP 數
    的頭像 發表于 08-05 11:54 ?5021次閱讀

    windows下C語言使用curl庫訪問HTTP下載文件

    cURL是一個利用URL語法在命令行下工作的文件傳輸工具,1997年首次發行。它支持文件上傳和下載,所以是綜合傳輸工具,但按傳統,習慣稱cURL為下載
    的頭像 發表于 09-17 15:57 ?2222次閱讀
    windows下C語言使用<b class='flag-5'>curl</b>庫訪問HTTP下載文件

    Linux系統如何安裝curl

    `curl`是用于在本地計算機與遠程服務器之間傳輸數據的命令行工具。使用curl時您可以使用HTTP,HTTPS, SCP , SFTP和FTP等協議下載或上傳數據。
    的頭像 發表于 12-06 16:53 ?1.7w次閱讀

    curl命令接入onenet簡單使用

    注意的是,格式一定得正確無誤。那么就接下來進入正題吧。先簡單介紹下什么是curl吧: curl是利用url語法在命令行方式下工作的開源文件傳輸工具(來自百度百科)。curl是一種簡單有
    發表于 10-20 17:11 ?0次下載
    <b class='flag-5'>curl</b>命令接入onenet簡單使用

    linux開發板如何編譯curl

    準備開發環境 確保你的開發板已經安裝了編譯 curl 所需的工具,包括 gcc 、 make 和可能的其他庫。如果開發板是交叉編譯環境(即你在一個平臺上編譯用于另一個平臺的程序),你還需要確保安裝了適當的交叉編譯工具鏈。 2.
    的頭像 發表于 08-30 15:33 ?789次閱讀

    如何使用 cURL 測試 HTTP 協議

    cURL是一個強大的命令行工具,用于傳輸數據,支持多種協議,包括HTTP、HTTPS、FTP等。使用cURL測試HTTP協議可以幫助你理解HTTP請求和響應的工作原理,以及調試和驗證你的HTTP服務
    的頭像 發表于 12-30 09:26 ?247次閱讀

    curl wget bond:深入解析命令行工具的差異與應用場景

    curl curl 是一個用于與服務器進行數據傳輸的命令行工具。它支持多種協議,包括 HTTP、HTTPS、 FTP 等。 基本用法 獲取網頁內容: curl http://examp
    的頭像 發表于 01-24 09:20 ?73次閱讀
    百家乐扑克多少张| 职业百家乐官网的玩法技巧和规则 | 百家乐官网永利娱乐网| 宝格丽百家乐官网娱乐城| 百家乐官网什么牌最大| 兴国县| 百家乐官网摇色子网站| 百家乐官网开户百家乐官网技巧 | 百家乐官网六合彩3535 | 老k百家乐的玩法技巧和规则| 百家乐高级技巧| 什么是百家乐的大路| 威尼斯人娱乐赌博| 大发888通宝| 澳门赌场色情| 百家乐官网体育博彩| 百家乐官网庄闲概率| 百家乐官网微笑玩| 九州百家乐官网的玩法技巧和规则 | 南溪县| 百家乐官网马宝| 娱乐城百家乐官网的玩法技巧和规则 | 优博娱乐城| 大世界百家乐官网现金网| 百家乐官网单注打法| 百家乐庄闲分布概率| 新天地百家乐的玩法技巧和规则 | 百家乐官网网络游戏信誉怎么样| 百家乐打印机分析| 百家乐翻天下载| 百家乐网页游戏| 9人百家乐官网桌布| 免费百家乐官网平预测软件| 郑州百家乐高手| 大发888 有斗地主吗| 百家乐官网视频双扣| 大家旺百家乐官网的玩法技巧和规则| 百家乐游戏网站| 娱网棋牌大厅| 百家乐官网投注技巧公式| 乐天百家乐官网的玩法技巧和规则|