openssl 使用非阻塞 bio

2021-07-10 09:00:46 字數 2540 閱讀 8429

很久沒有更新這個blog了,原因有多個,期間也換了多個blog平台,最後還是回到了csdn的懷抱中,算是乙個輪迴吧。作為久別重逢的開篇之作,本文是對最近一段時間,使用 openssl 的乙個坑點總結。

在專案中需要訪問 https 加密的網頁,為了保證併發性,需要用到非阻塞的 socket,搜尋發現,這種使用場景的相關介紹不是很多,所以這裡記錄一下使用的過程。

在專案中,所使用的 ssl 庫是老牌 sll 庫 —— openssl。所使用的 io多路復用 技術是 epoll。

在 socket 建立 tcp 連線之後,需要繫結 socket 控制代碼在 ssl 中

讀取,傳送資料,使用 ssl 庫的方法,替代 linux 系統呼叫

關閉連線前,需要先執行 ssl 關閉流程

首先,開啟 socket 控制代碼,然後設定必要的屬性

int sock_fd = -1;

int flags = -1;

sock_fd = socket(af_inet, sock_stream, 0);

flags = fcntl(sockfd, f_getfl, 0);

fcntl(sockfd, f_setfl, flags | o_nonblock);

然後,將控制代碼加入 epoll 的管理

epoll_event ev;

ev.events = epollin | epollout | epollet

ev.data.ptr = your_ev_info;

epoll_ctl(epfd, epoll_ctl_add, url_item->sockfd, &ev);

現在,可以開始真正的連線過程了,與普通的 tcp 連線一樣,呼叫 connect 系統呼叫。在非阻塞 io 中,需要通過 connect 的返回值和 errno 來判斷連線狀態,採取不同的策略

struct sockaddr_in serv_addr;

if (connect(sock_fd, (sockaddr *) & serv_addr, sizeof (sockaddr)) < 0)

} else

如果沒有立刻連線成功,在成功後,會觸發epoll,我們需要在your_ev_info中,需要儲存現在的狀態,以便在epoll_wait之後,通過狀態來決定需要呼叫的函式。這些屬於 epoll 的細節了,在此不展開說。

假設,現在已經連線成功,則開始做 ssl 握手之前的準備工作。

ssl_ctx *ssl_ctx;

ssl *ssl;

ssl_ctx = ssl_ctx_new(tlsv1_method());

ssl = ssl_new(url_item->ssl_ctx);

ssl_set_mode(url_item->ssl, ssl_mode_enable_partial_write);

// 繫結 ssl 和 socket 控制代碼

ssl_set_fd(ssl, sock_fd);

這一步之所以和後面的 ssl 握手過程分開,是因為 ssl 握手在非阻塞io 的情況下,有可能會被呼叫多次,而這部分只需要一次呼叫即可。

現在開始 ssl 握手

int ssl_conn_ret = ssl_connect(ssl);

if (1 == ssl_conn_ret) else

if (-1 == ssl_conn_ret)

} else

ssl_free(ssl);

ssl_ctx_free(ssl_ctx);

}

在沒有立刻握手成功的時候,需要在 epoll 觸發後,在次呼叫此段**,來繼續握手的過程。

至此,建立連線的過程就完成了。

由於傳送與讀取資料都有可能沒有完全完成我們所指定的長度,所以需要判斷對應返回值,來決定是否繼續傳送或讀取

// 傳送資料

int ret = ssl_write(ssl, buf + last_write_pos, buf_len - last_write_pos);

// 讀取資料

int ret = ssl_read(ssl, buf + last_read_pos, buf_len - last_read_pos);

// 關閉 ssl 連線

ssl_shutdown(ssl);

ssl_free(ssl);

ssl_ctx_free(ssl_ctx);

// 然後關閉 socket

close(sock_fd);

在使用過程中,整體流程是十分順利的。乙個最重要的點是關於 openssl 與 epoll 的邊緣觸發配合的問題。

當需要使用 epoll 的邊緣觸發時,一定要注意,ssl_read 最多隻會讀取乙個完整的加密段,所以,當一次可以讀取的資料量大於此值時,需要迴圈呼叫 ssl_read 直到讀取失敗為止。否則,就會導致在緩衝區中的資料沒有完全讀取的情況。

阻塞 非阻塞

阻塞和非阻塞指 的是在接收和傳送時是否等待動作完成才返回 舉例 阻塞 block 是指,你撥通某人 的 但是此人不在,於是你拿著 等他回來,其間不能再用 非阻塞 nonblock 是指,你撥通某人 的 但是此人不在,於是你結束通話 待會兒再打。至於到時候他回來沒有,只有打了 才知道。即所謂的 輪詢 ...

阻塞非阻塞

阻塞和非阻塞 阻塞 可用在assign語句和always語句中,表示只要源訊號發生變化,目標訊號就立刻完成賦值操作,在always塊中,結果與語句順序有關,在always塊中是順序關係 非阻塞 只能用在always語句中,表示該語句結束時完成賦值操作,結果與語句順序無關,並行關係 可以這樣理解 阻塞...

socket使用非阻塞connect

在使用tcp的connect呼叫時,預設是使用阻塞方式,當伺服器當前不可用時,connect會等待 內部在重試?直到超時時間到達,而這個超時時間是系統核心規定的,不能使用setsocketopt來設定。在碰到伺服器不可用,上層邏輯進行重試時,如果超時時間過長,會產生卡死的感覺,使用者體驗也不佳,所以...