openssl之BIO系列之18---接受(accept)類型BIO,openssl18---
接受(accept)類型BIO
---根據openssl doc\crypto\bio_s_accept.pod翻譯和自己的理解寫成
(作者:DragonKing, Mail: wzhah@263.net ,發佈於:http://gdwzh.126.com之o
penssl專業論壇)
接受(accept)類型的BIO跟串連(connect)類型BIO是相對應的,它封裝了Socke
t的accept方法及其相關的一些操作,使得能夠對不同的平台使用同一的函數進行操作。
其定義的相關函數如下(openssl\bio.h):
BIO_METHOD * BIO_s_accept(void);
#define BIO_set_accept_port(b,name) BIO_ctrl(b,BIO_C_SET_ACCEPT,0,(char
*)name)
#define BIO_get_accept_port(b) BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,0)
BIO *BIO_new_accept(char *host_port);
#define BIO_set_nbio_accept(b,n) BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(n)?"a":
NULL)
#define BIO_set_accept_bios(b,bio) BIO_ctrl(b,BIO_C_SET_ACCEPT,2,(char
*)bio)
#define BIO_set_bind_mode(b,mode) BIO_ctrl(b,BIO_C_SET_BIND_MODE,mode,N
ULL)
#define BIO_get_bind_mode(b,mode) BIO_ctrl(b,BIO_C_GET_BIND_MODE,0,NULL
)
#define BIO_BIND_NORMAL 0
#define BIO_BIND_REUSEADDR_IF_UNUSED 1
#define BIO_BIND_REUSEADDR 2
#define BIO_do_accept(b) BIO_do_handshake(b)
【BIO_s_accept】
該函數返回一個接受(Accept)類型的BIO_METHOD結構,其定義如下:
static BIO_METHOD methods_acceptp=
{
BIO_TYPE_ACCEPT,
"socket accept",
acpt_write,
acpt_read,
acpt_puts,
NULL, /* connect_gets, */
acpt_ctrl,
acpt_new,
acpt_free,
NULL,
};
通過該結構我們可以一目瞭然的知道該方法支援什麼I/O操作,在本類型中,BIO_g
ets的操作是不支援的。跟串連(connect)類型BIO一樣,openssl也定義了一個維護接
受Socket資訊的結構,在此我也不再詳細介紹該結構,大家參考bss_acpt.c檔案。
本類型的BIO對各種平台的TCP/IP的accept做了封裝,所以在使用的時候就可以同一
的使用BIO的規則進行操作,而不用擔心因為不同的平台帶來程式改寫或增加移植的工作
量,這也是BIO很重要的一個目的。
BIO_read和BIO_write函數操作調用了底層平台串連的I/O相關操作,如果這時候沒
有串連建立,連接埠設定正確,那麼該BIO就會等待串連的建立。事實上,當一個串連建立
的時候,一個新的socket類型BIO就會被建立並附加到BIO鏈中,形成accept->socket的
BIO結構,所以這時候對初始化了的接受socket進行IO操作就會導致它處於等待串連建立
的狀態。當一個接受類型的BIO在BIO鏈的末尾的時候,在處理I/O調用之前它會先等待一
個串連的建立;如果不是在末尾,那麼它簡單的把I/O調用傳到下一個BIO。
如果接受(accept)類型BIO的關閉標誌設定了,那麼當BIO被釋放的時候,該BIO鏈
上任何活動的串連和socket都會被關閉。
BIO_get_fd和BIO_set_fd可以用來取得和設定該串連的socket描述符,詳細情況參
看《BIO系列之12---描述符(fd)類型BIO》。
【BIO_set_accept_port】
該函數使用字串名來設定接受連接埠。其形式為“host:port”,“host”是要使用的
介面,“port”是連接埠。這兩部分都可以為“*”,這時候表示可以使用任意介面和連接埠
。這裡的連接埠的字串含義跟串連(connect)類型BIO裡面定義的一樣,可以為數字形
式的,可以使用getservbyname函數去匹配得到,還可以使用字串的表,請參看串連型
BIO說明的相關部分。
【BIO_new_accept】
該函數將BIO_new和BIO_set_accept_port函數放在一個函數裡面調用,建立一個新
的接受(accept)類型的BIO。
【BIO_set_nbio_accept】
該函數設定接受socket是否為阻塞模式(預設),如果參數n為0,為阻塞模式,n為
1則為非阻塞模式。
【BIO_set_accept_bios】
該函數用來設定一個BIO鏈,當接受到一個串連的時候,這個設定好的BIO鏈就會被
複製或附加到整個BIO鏈上來。有的時候這中處理方法是非常好的,比如,如果每個串連
都需要一個緩衝區或SSL類型的BIO,這時候使用本函數就省了很多麻煩了。需要注意的
是,在調用本函數後,這個設定的鏈上的BIO不能自己釋放,在接受(accept)BIO被釋
放後,它們會自動釋放。
當該函數調用的時候,其設定的BIO鏈位於接受BIO和socket類型的BIO之間,即形成
:accept->otherbios->socket的新的BIO鏈。
【BIO_set_bind_mode和BIO_get_bind_mode】
這兩個函數用來設定和取得目前的繫結模式。如果設定為BIO_BIND_NORMAL(預設)
,那麼另外一個socket就不能綁定到同一個連接埠。如果設定為BIO_BIND_REUSEADDR ,那
麼另外的socket可以綁定到同一個連接埠。如果設定為BIO_BIND_REUSEADDR_IF_UNUSED,
那麼首先會嘗試以BIO_BIND_NORMAL的模式綁定到連接埠,如果失敗了而且連接埠沒有使用,
那麼就會使用BIO_BIND_REUSEADDR 模式綁定到連接埠。
【BIO_do_accept】
該函數有兩個功能,當它在接受(accept)BIO設定好之後第一被調用的時候,它會
建立一個接受socket並把它跟地址綁定;第二次被調用的時候,它會等待串連的到來。
【注意】
如果伺服器端希望可以處理多個串連的情況(通常都是這樣的),那麼接受BIO必須
能夠用來處理以後的串連,這時候可以這樣做:
等待到一個串連後,調用
connection=BIO_pop(accept)
這樣,cnnection會包含一個最近建立的串連的BIO,accept就再次成為一個獨立的
BIO,可以用來處理其它串連了。如果沒有其它串連建立,那麼就可以使用BIO_free釋放
該BIO。
當然,如果只有一個串連需要處理,也可以使用串連BIO本身來進行I/O操作。但是
一般來說不推薦這樣做,因為這時候該接受BIO依然處於接受其它串連建立的狀態。這可
以使用上述的方法解決。
【例子】
這個例子在4444連接埠接受了兩個串連,發送資訊到每個串連上然後釋放他們。
BIO *abio, *cbio, *cbio2;
ERR_load_crypto_strings();
abio = BIO_new_accept("4444");
/* 首先調用BIO_accept啟動接受BIO */
if(BIO_do_accept(abio) <= 0) {
fprintf(stderr, "Error setting up accept\n");
ERR_print_errors_fp(stderr);
exit(0);
}
/* 等待串連建立*/
if(BIO_do_accept(abio) <= 0) {
fprintf(stderr, "Error accepting connection\n");
ERR_print_errors_fp(stderr);
exit(0);
}
fprintf(stderr, "Connection 1 established\n");
/* 返回串連的BIO*/
cbio = BIO_pop(abio);
BIO_puts(cbio, "Connection 1: Sending out Data on initial connection\n"
);
fprintf(stderr, "Sent out data on connection 1\n");
/* 等待另一個串連的建立 */
if(BIO_do_accept(abio) <= 0) {
fprintf(stderr, "Error accepting connection\n");
ERR_print_errors_fp(stderr);
exit(0);
}
fprintf(stderr, "Connection 2 established\n");
/* 關閉串連BIO,不再接受串連的建立 */
cbio2 = BIO_pop(abio);
BIO_free(abio);
BIO_puts(cbio2, "Connection 2: Sending out Data on second\n");
fprintf(stderr, "Sent out data on connection 2\n");
BIO_puts(cbio, "Connection 1: Second connection established\n");
/* 關閉這兩個串連 */
BIO_free(cbio);
BIO_free(cbio2);