很久沒有更新這個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來設定。在碰到伺服器不可用,上層邏輯進行重試時,如果超時時間過長,會產生卡死的感覺,使用者體驗也不佳,所以...