非阻塞/異步(epoll) openssl

前段時間在自己的異步網絡框架handy中添加openssl的支持,當時在網絡上搜索了半天也沒有找到很好的例子,後來自己慢慢的摸索,耗費不少時間,終於搞定。因此把相關的資料整理一下,並給出簡單的例子,讓後學者可以少費些力氣。

同步的openssl調用網上已經有許多的例子,這裏就不再詳細介紹,大家也可以直接讀源代碼:

同步客戶端:https://github.com/yedf/openssl-example/blob/master/sync-ssl-cli.cc
該例子連接www.openssl.com:443,發送一個Http請求,並打印結果中的前256個字符

同步服務器端:https://github.com/yedf/openssl-example/blob/master/sync-ssl-svr.cc
該例子監聽本地的443端口,並返回一個簡單http響應

下面詳細介紹非阻塞調用

1. 初始化SSL庫
SSL_load_error_strings ();
SSL_library_init ();
sslContext = SSL_CTX_new (SSLv23_method ());

//server端需要初始化證書與私鑰
string cert = "server.pem", key = "server.pem";
r = SSL_CTX_use_certificate_file(g_sslCtx, cert.c_str(), SSL_FILETYPE_PEM);
r = SSL_CTX_use_PrivateKey_file(g_sslCtx, key.c_str(), SSL_FILETYPE_PEM);
r = SSL_CTX_check_private_key(g_sslCtx);


2. 非阻塞方式建立tcp連接(網上有很多epoll相關例子)

3. 使用已建立連接的socket初始化ssl
ch->ssl_ = SSL_new (g_sslCtx);
int r = SSL_set_fd(ch->ssl_, ch->fd_);
服務器端 SSL_set_accept_state(ch->ssl_);
客戶端 SSL_set_connect_state(ch->ssl_);

4. epoll_wait後,如果SSL相關的socket有讀寫事件需要處理則進行SSL握手,直到握手完成
int r = SSL_do_handshake(ch->ssl_);
if (r == 1) { // 若返回值爲1,則SSL握手已完成
  ch->sslConnected_ = true;
  return;
}
int err = SSL_get_error(ch->ssl_, r);
if (err == SSL_ERROR_WANT_WRITE) { //SSL需要在非阻塞socket可寫時寫入數據
  ch->events_ |= EPOLLOUT; 
  ch->events_ &= ~EPOLLIN;
} else if (err == SSL_ERROR_WANT_READ) { //SSL需要在非阻塞socket可讀時讀入數據
  ch->events_ |= EPOLLIN; //等待socket可讀
  ch->events_ &= ~EPOLLOUT; //暫時不關注socket可寫狀態
} else { //錯誤
  ERR_print_errors(errBio);
}

5. 握手完成後,進行SSL數據的讀寫
SSL_write(con->sslHandle, text, len);
SSL_read(con->sslHandle, buf, sizeof buf);

詳細可運行的例子參看
https://github.com/yedf/openssl-example/blob/master/async-ssl-svr.cc
https://github.com/yedf/openssl-example/blob/master/async-ssl-cli.cc

handy已經對openssl進行了封裝,並且給出了例子,詳見
https://github.com/yedf/handy-ssl

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章