Linux使用epoll異步發送http請求

2020-12-16 閒聊代碼

http是基於tcp的協議,在發送http請求之前,要先與伺服器建立tcp連接,然後才可以發送HTTP請求。

HTTP請求的頭部,就是一些以\r\n分割的字符串。

第一行為GET、POST方法,之後的每一行為冒號分割的鍵值對,表示http請求的一些信息。

最後以一行單獨的\r\n與請求的正文分割。

如果沒有正文,則最後這行\r\n就表示請求的結束。

它與上一行的\r\n正好構成連續的2個回車換行,這可以用來判斷http頭的結束和正文的開始。

把這行字符串發送到伺服器,然後讀取返回結果,就是伺服器的應答。

應答可能是個html文件,也可以是其他文件,或者一個flv的動態視頻流(直播一般使用http-flv)。

實際的工程代碼中,為了避免connect連接期間無法響應用戶操作,一般使用異步socket。

只需給epoll_wait設置10毫秒超時,用戶是感知不到的,但是同步socket多長時間返回是不確定的。

Linux異步socket的步驟:

1,在socket()函數獲取文件描述符時設置SOCK_NONBLOCK標示,也可以在之後用fcntl()函數設置。

2,connect()函數將返回-1並提示EINPROGRESS的錯誤碼,表示連接正在進行。

3,把socket文件描述符加入epoll監控寫事件EPOLLOUT。

4,在epoll提示可寫時,用getsockopt在SOL_SOCKET層上讀取SO_ERROR錯誤碼,0表示連接成功,-1表示失敗。

tcp連接成功之後,就可以構造一個http的請求字符串,發送給伺服器了。

之下幾張圖是演示的代碼。

先創建一個epoll的文件描述符,然後創建一個異步socket。

domain參數為AF_INET,表示ipv4。

type參數為SOCK_STREAM表示tcp,SOCK_NONBLOCK表示非阻塞。

最後一項參數為0。

然後設置連接地址,圖中是我本機的nginx伺服器地址。

16位埠使用網絡字節序(大端),用htons()轉換。

IP位址用inet_addr()轉換,這裡是本機的本地環回地址127.0.0.1。

調用connect()連接,如果返回值為-1時查看errno是否為EINPROGRESS,不是則出錯。

這裡不大可能直接返回0表示連接成功,因為要等待伺服器響應,有個TCP三次握手過程,而connect()函數發送了TCP_SYN包就要返回,收到伺服器的TCP_ACK包(一般同時設置SYN標示)才算成功。伺服器端則要再收到客戶端的TCP_ACK包才認為成功。

epoll監控它的讀事件EPOLLIN、寫事件EPOLLOUT、對端關閉事件EPOLLRDHUP。

EPOLLET,邊沿觸發,即socket的讀寫狀態有變化時觸發事件。

EPOLLLT,默認是水平觸發,即可讀可寫時一直觸發事件。

常用的是EPOLLET,邊沿觸發,所以可讀時要一次讀完socket緩衝區的數據,直到recv()提示EAGAIN錯誤碼。

可寫時也要一次寫完數據,或者send()提示EAGAIN錯誤碼。

while循環為epoll的主循環,不斷的使用epoll_wait監控讀寫事件。

返回值< 0表示出錯,= 0表示超時。

這裡只有一個文件描述符需要監控,正常時返回1,不需要遍歷了,加個assert斷言讓代碼邏輯保持完整就行。

這兩個assert如果真被觸發了?!

那一定是我手抖了,把==打成了!=(笑

如果是還沒連接成功,讀取連接狀態,err碼為0表示成功,其他表示失敗。

連接成功之後,組裝一個http的請求頭,去GET nginx伺服器的根目錄/。

只需要設置這麼幾項就行,見代碼圖片。

在socket可寫時把它發送到伺服器,不一定能一次發送完,所以記錄總長度len和發送的偏移量pos。

如果send()出錯,這裡只能是EAGAIN或者EINTR,一個表示socket不可寫需要等下次發送,一個表示send()函數被打斷也是需要再次發送,其他錯誤碼都是真出錯了。

發送成功之後,根據epoll提示的讀事件,讀取伺服器的響應。

一次不一定能讀完,這裡要解析http響應頭來確定讀取結果。

先獲取Content-Length項的長度,然後判斷http正文的長度,如果這兩個相等則讀完了,否則要繼續讀。

如果是chunk模式,則根據每個chunk前的長度值確定該chunk的大小,最後一個chunk大小是0。

這裡直接把第一次recv到的數據打出來,沒有進一步解析。

讀取到的結果,本機的nginx,一次就讀全了。

相關焦點

  • Linux使用epoll控制多個socket發送http請求
    在客戶端使用epoll控制多個socket發送數據,與在伺服器上是類似的,也是把一個連續的同步過程拆成多個非阻塞的階段,在一個線程內實現高並發,而不是開多個線程。客戶端使用多個socket異步高並發,一般是對伺服器做壓力測試的代碼。
  • easyhttp v1.1發布,新增異步並發請求
    EasyHttp 是一個輕量級、語義化、對IDE友好的HTTP客戶端,支持常見的HTTP請求、異步請求和並發請求,讓你可以快速地使用 HTTP 請求與其他 Web 應用進行通信。
  • 宋寶華: 資料庫為什麼有可能喜歡Linux AIO(異步I/O)?
    我們都知道Linux的IO模型有阻塞、非阻塞、SIGIO、多路復用(select,epoll)、AIO(異步I/O)等。資料庫可能比較傾向於使用AIO。從時序上面來講,AIO是用戶應用發起IO請求io_submit()後,它就不需要去等待,讓後臺給它搞定讀寫。
  • epoll和select的區別
    但select,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。
  • linux開發各種I/O操作簡析,以及select、poll、epoll機制的對比
    這裡我們能夠看到poll使用的優勢,select,poll,epoll都是IO多路復用的機制。I/O多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。
  • 從linux源碼看epoll
    從linux源碼看epoll前言在linux的高性能網絡編程中,繞不開的就是epoll。
  • io_submit:Linux內核新加入的epoll替代方案
    該補丁由Christoph Hellwig,還提議將Linux AIO接口配合網絡套接字等一起使用。Linux的AIO是最初是設計用於磁碟異步IO的接口。文件與網絡套接字是大相逕庭的東西,可以用Linux AIO 接口,將其統一起來呢?
  • Golang是怎麼利用epoll的
    使用Golang可以輕鬆地為每一個TCP連接創建一個協程去服務而不用擔心性能問題,這是因為Go內部使用goroutine結合IO多路復用實現了一個「異步」的IO模型,這使得開發者不用過多的關注底層,而只需要按照需求編寫上層業務邏輯。這種異步的IO是如何實現的呢?下面我會針對Linux系統進行分析。
  • 框架篇:見識一下linux高性能網絡IO+Reactor模型
    單個socket時,使用一個線程即可高效處理;然而如果是10K個socket連接,或者更多,我們如何做到高性能處理?相對同步IO,異步IO在用戶進程發起異步讀(aio_read)系統調用之後,無論內核緩衝區數據是否準備好,都不會阻塞當前進程;在aio_read系統調用返回後進程就可以處理其他邏輯socket數據在內核就緒時,系統直接把數據從內核複製到用戶空間,
  • 高性能Swoole擴展的安裝與使用
    Swoole 概述Swoole是面向生產環境的PHP異步網絡通信引擎。使用純C語言編寫(Swoole 4開始逐漸改為通過C++編寫),提供了PHP語言的異步多線程伺服器、異步TCP/UDP網絡客戶端、異步MySQL、異步Redis、資料庫連接池、AsyncTask、消息隊列、毫秒定時器、異步文件讀寫、異步DNS查詢。
  • 用JavaScript發出HTTP請求的不同方法
    全文共2678字,預計學習時長15分鐘使用JavaScript時,總會有各種需要發出調用請求的情況,進行ajax調用什麼技術更適合呢?通用的Request和Response接口提供了一致性,而Promises允許更容易的連結和沒有回調的異步/等待。Fetch簡潔,優雅且易於理解,但是還有其他不錯的選擇,本文將簡要的含義、語法以及利弊。以下代碼展示了使用不同替代方法的基本HTTP GET和POST示例。
  • Nginx---高性能的HTTP和反向代理web伺服器
    #現在在linux 2.6內核下開啟文件打開數為65535,worker_rlimit_nofile就相應應該填寫65535。#這是因為nginx調度時分配請求到進程並不是那麼的均衡,所以假如填寫10240,總並發量達到3-4萬時就有進程可能超過10240了,這時會返回502錯誤。
  • Java網絡編程——發送HTTP請求到伺服器
    當Java程序需要向伺服器發送請求或讀取伺服器數據時,使用URLConnection類是比較好的選擇。URLConnection類封裝了與伺服器互動操作的方法,通過它可以建立與伺服器的遠程連接,檢查伺服器資源的屬性,向伺服器發送請求並接收伺服器返回的數據。
  • .NET異步編程:使用CPS及yield實現異步
    【IT168 技術】在上一篇文章(.NET中的異步編程:傳統的異步編程)中我們圍觀了傳統的異步編程,感受到了異步編程不是簡單的事情。傳統的異步方式將本來緊湊的代碼都分成兩部分,不僅僅降低了代碼的可讀性,還讓一些基本的程序構造無法使用,所以大部分開發人員在遇到應該使用異步的地方都忍痛割愛。
  • jmeter(五)HTTP請求
    這些JMeter提供的配置元件中的HTTP屬性管理器用於儘可能模擬瀏覽器行為,在HTTP協議層上發送給被測應用的http請求(1)HTTP Request Defaults(請求默認值)    用於設置其作用範圍內的所有HTTP的默認值,可被設置的內容包括HTTP請求的host、埠、協議等(2)HTTP Authorization Manager(授權管理器
  • Fetch還是Axios——哪個更適合HTTP請求?
    前端開發最重要的部分之一是通過發出HTTP請求與後端進行通信,我們有幾種方法可以異步地在Javascript中進行API調用。
  • Fetch還是Axios,哪個更適合HTTP請求?
    來源 | https://medium.com作者 | Harsh Patel前端開發最重要的部分之一是通過發出HTTP請求與後端進行通信,我們有幾種方法可以異步地在