21. 從零用Rust編寫正反向代理,tokio竟然這樣對待socket!

来源:https://www.cnblogs.com/wmproxy/archive/2023/11/01/17802214.html
-Advertisement-
Play Games

wmproxy wmproxy已用Rust實現http/https代理, socks5代理, 反向代理, 靜態文件伺服器,四層TCP/UDP轉發,內網穿透,後續將實現websocket代理等,會將實現過程分享出來,感興趣的可以一起造個輪子 項目地址 國內: https://gitee.com/tic ...


wmproxy

wmproxy已用Rust實現http/https代理, socks5代理, 反向代理, 靜態文件伺服器,四層TCP/UDP轉發,內網穿透,後續將實現websocket代理等,會將實現過程分享出來,感興趣的可以一起造個輪子

項目地址

國內: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

有請主角上場

Socket是集萬千寵愛為一身的王子,在操作系統的王國里,他負責對外的所有通訊,所以要想溝通鄰國的公主必須經過他,所以大家對他都是萬般友好。

這天一個Rust城市裡的大臣tokio對他發起了邀請,邀請他來參觀嚴謹的邏輯莊園。

tokio莊園

莊園中的各成員對即將到來的王子議論紛紛。

  大管家mio說:“大家都想想等下怎麼向socket王子介紹自己,好讓他配合大家的工作。”

  大管家miotokio的基石,一切和王國打交道的都交由他去打理,他是保證莊園高效運轉的關鍵,此刻他準備好了歡迎宴會。

  在宴會上,socket聽說tokio莊園是這座城市非同步運行的重要基石,就很好奇的讓大伙介紹介紹下怎麼工作的。

  莊園主tokio就說:“我是依靠著大管家mio幫我負責處理底層的事,Waker來提醒我有新的事情,PollEvented來幫我管理事件的。下麵先讓mio來介紹下。”

  管家mio說:“我負責收集莊園中的所有信息,他們告訴我他們要關心的什麼比較,比如您的到來(可讀),或者您有什麼話想說(可寫),我會負責和王國的底層進行溝通,我在這個國家用的是epoll,據說在遙遠的另一個國家用的是iocp,如果有相應的需求,我將會通知Waker,由他去提醒莊主來及時的處理,這場宴會也是提前得到通知而進行準備的。”

  通知Waker說:“我所做的事情就是微不足道,我的對接對象是PollEvented,當他關心讀事件,我會向mio去發起poll_read請求,如果此時mio那邊已經知道有新的消息了,那我就直接把他們讀出來交給民眾Poll::Ready,如果此時還沒有新消息,那我會告訴管家,有新消息的時候通知我Poll::Pending,此時我就在這裡等待,直到有新的消息到達我就通知給民眾。當他關心寫事件,我會向mio請求poll_write請求,後續的和收消息的一致。現在給你們展示下包裝了一層我的Context和我能換醒的虛表。”

/// 這個在代碼里就是經常看到,它就是我的一層淺封裝啦。
pub struct Context<'a> {
    waker: &'a Waker,
    _marker: PhantomData<fn(&'a ()) -> &'a ()>,
}
/// 我通過他來控制回調,保證喚醒的時候能正確的通知
pub struct RawWakerVTable {
    clone: unsafe fn(*const ()) -> RawWaker,
    wake: unsafe fn(*const ()),
    wake_by_ref: unsafe fn(*const ()),
    drop: unsafe fn(*const ()),
}

  事件PollEvented說:“莊主要處理的事情太多了,而有些事情又需要等待一層層的反饋,他沒法把精力放在一件事情上一直等待,所以就有了我出馬,莊主告訴我他關心什麼事,我就把它記下來,這樣子莊主就可以去處理其它的事,等事件到來的時候我就告訴莊主,這樣子莊主就可以高效的處理所有的事件。”

王子覺得他們說了一堆有點啰嗦

  “帶我看看你們實際的工坊,我要實地考查下。”王子說。

莊主就帶著王子來到了,受理工坊,受理工坊正在處理建立受理點:

TcpListener::bind(addr).await

受理點的內容就是PollEvent

pub struct TcpListener {
    io: PollEvented<mio::net::TcpListener>,
}

當他接受新的受理者的時候:

pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
    let (mio, addr) = self
        .io
        .registration()
        .async_io(Interest::READABLE, || self.io.accept())
        .await?;

    let stream = TcpStream::new(mio)?;
    Ok((stream, addr))
}

他向PollEvent註冊了可讀事情有的時候通知他,此時PollEvent就建立了一個Waker對象,當有符合條件的時候就來告訴他:

/// 建立一個可讀的Future對象
fn readiness_fut(&self, interest: Interest) -> Readiness<'_> {
    Readiness {
        scheduled_io: self,
        state: State::Init,
        waiter: UnsafeCell::new(Waiter {
            pointers: linked_list::Pointers::new(),
            waker: None,
            is_ready: false,
            interest,
            _p: PhantomPinned,
        }),
    }
}

底層有mio和系統交互,一旦有數據就通知Waker,他建在runtime/io/driver.rs上面

fn turn(&mut self, handle: &Handle, max_wait: Option<Duration>) {
    let events = &mut self.events;
    // 高效的監聽埠
    match self.poll.poll(events, max_wait) {
        Ok(_) => {}
    }

    for event in events.iter() {
        let token = event.token();

        if token == TOKEN_WAKEUP {
        } else if token == TOKEN_SIGNAL {
        } else {
            let ready = Ready::from_mio(event);
            let ptr: *const ScheduledIo = token.0 as *const _;
            
            let io: &ScheduledIo = unsafe { &*ptr };
            io.set_readiness(Tick::Set(self.tick), |curr| curr | ready);
            // 有相應的事件,進行喚醒然後通知上層處理相應的事件
            io.wake(ready);

        }
    }

}

然後看到Waker工坊上處理:

pub(crate) fn wake_all(&mut self) {
    assert!(self.curr <= NUM_WAKERS);
    while self.curr > 0 {
        self.curr -= 1;
        let waker = unsafe { ptr::read(self.inner[self.curr].as_mut_ptr()) };
        waker.wake();
    }
}
pub fn wake(self) {
    // 存在的回調函數及對應的數據,好進行調用
    let wake = self.waker.vtable.wake;
    let data = self.waker.data;

    crate::mem::forget(self);

    // 用回調函數的方式處理剛等待執行的線程
    unsafe { (wake)(data) };
}

最後又回到了受理工坊,我們知道了一個新的來源TcpStream的到來,期間在等待的時候,我們可以去處理其它的事情,不至於空有許多人力物力,卻在等我的寶的事情到來沒法快速處理事情。

  王子說:“在只有一個受理的時候你們這麼高效,如果有同時多個受理,又需要在處理完訪問相同的數據,你們又能處理嗎?”

  莊主說:“那麼接下來就讓我帶你參觀下Poll工坊,他用同步的方式可以同時處理多個鏈接。”

參觀完非同步工坊,莊主又帶著王子來到了

只見Poll工坊大屏幕上就出現了一個例子:

#[tokio::main]
async fn main() -> std::io::Result<()> {
    use std::{future::poll_fn, task::Poll, pin::Pin};
    use tokio::net::TcpListener;
    let mut listeners = vec![];
    // 同時監聽若幹個埠
    for i in 1024..1099 {
        listeners.push(TcpListener::bind(format!("127.0.0.1:{}", i)).await?);
    }
    loop {
        let mut pin_listener = Pin::new(&mut listeners);
        // 同時監聽若幹個埠,只要有任一個返回則返回數據
        let fun = poll_fn(|cx| {
            for l in &*pin_listener.as_mut() {
                match l.poll_accept(cx) {
                    v @ Poll::Ready(_) => return v,
                    Poll::Pending => {},
                }
            }
            Poll::Pending
        });
        let (conn, addr) = fun.await?;
        println!("receiver conn:{:?} addr:{:?}", conn, addr);
    }
}

他可快速的在函數中同時等待多個埠數據,這種同步的邏輯可以在複雜結構時很方便的書寫代碼的邏輯。

王子看完後:“現在你的處理能力已經高效又靈活,真正的可甜可咸了,把我的能力發揮完全又簡化了操作。”

點擊 [關註][在看][點贊] 是對作者最大的支持


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JSON 是一種用於存儲和交換數據的語法。JSON 是文本,使用 JavaScript 對象表示法編寫。 Python 中的 JSON Python 有一個內置的 json 包,可用於處理 JSON 數據。 示例:導入 json 模塊: import json 解析 JSON - 從 JSON 轉換 ...
  • 創建名為spring_mvc_ajax的新module,過程參考9.1節和9.5節 10.1、SpringMVC處理Ajax請求 10.1.1、頁面請求示例 <input type="button" value="測試SpringMVC處理Ajax請求" onclick="testAjax()"> ...
  • 一、 前言 ​ 最近在看tomcat connector組件的相關源碼,對Nio2的非同步回調過程頗有興趣,平時讀源碼不讀,自己讀的時候很多流程都沒搞明白,去查網上相關解析講的給我感覺也不是特別清晰,於是就自己慢慢看源碼,以下是我自己的見解,因為開發經驗也不多,剛成為社畜不久,有些地方講錯如果有大佬看 ...
  • 在如今這個信息爆炸的時代,短視頻成為了一種非常受歡迎的娛樂方式。而在短視頻中,各種搞笑的內容更是大受歡迎。因此,開發一個能夠讓人們笑翻天的笑話短視頻介面就成為了一個非常有趣的項目。本文將介紹如何使用挖數據平臺的API來開發一個簡單的笑話短視頻介面,並提供代碼說明。 API介紹 挖數據平臺提供了一個非 ...
  • 中斷系統:是執行和管理中斷的邏輯結構 外部中斷:是眾多能產生中斷的外設之一 中斷:指的是中斷源(中斷通道),中斷產生CPU暫停正在執行程式,去執行中斷程式,然後返回。提高效率 F1系列的STM32有68個中斷源,不同系列需要看手冊 EXTI(外部中斷)、TIM、ADC、USART、SPI、I2C、R ...
  • 同系列文章 QT中級(1)QTableView自定義委托(一)實現QSpinBox、QDoubleSpinBox委托 QT中級(2)QTableView自定義委托(二)實現QProgressBar委托 QT中級(3)QTableView自定義委托(三)實現QCheckBox委托並且將QCheckBo ...
  • 背景 現代網路環境中,敏感數據的處理是至關重要的。敏感數據包括個人身份信息、銀行賬號、手機號碼等,泄露這些數據可能導致用戶隱私泄露、財產損失等嚴重後果。因此,對敏感數據進行脫敏處理是一種必要的安全措施。 比如頁面上常見的敏感數據都是加*遮擋處理過的,如下圖所示。 接下來本文將以Spring Boot ...
  • 來源:blog.csdn.net/mu_wind/article/details/113806680 初識線程池 我們知道,線程的創建和銷毀都需要映射到操作系統,因此其代價是比較高昂的。出於避免頻繁創建、銷毀線程以及方便線程管理的需要,線程池應運而生。 線程池優勢 降低資源消耗:線程池通常會維護一些 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...