BIO对(pair)类型BIO
---根据openssl doc/crypto/bio_s_bio.pod翻译和自己的理解写成
(作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之openssl专业论坛)
前面我们已经介绍过BIO对的概念,其实更进一步,BIO对也是作为一种source/sink类型的BIO来处理的,也就是说,BIO里面还提供了一种专门的BIO_METHO方法来处理BIO对的各种操作。BIO对类型的BIO各种相关的函数定义如下(openssl/bio.h):
BIO_METHOD *BIO_s_bio(void);
#define BIO_make_bio_pair(b1,b2) (int)BIO_ctrl(b1,BIO_C_MAKE_BIO_PAIR,0,b2)
#define BIO_destroy_bio_pair(b) (int)BIO_ctrl(b,BIO_C_DESTROY_BIO_PAIR,0,NULL)
#define BIO_shutdown_wr(b) (int)BIO_ctrl(b, BIO_C_SHUTDOWN_WR, 0, NULL)
#define BIO_set_write_buf_size(b,size) (int)BIO_ctrl(b,BIO_C_SET_WRITE_BUF_SIZE,size,NULL)
#define BIO_get_write_buf_size(b,size) (size_t)BIO_ctrl(b,BIO_C_GET_WRITE_BUF_SIZE,size,NULL)
int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2);
#define BIO_get_write_guarantee(b) (int)BIO_ctrl(b,BIO_C_GET_WRITE_GUARANTEE,0,NULL)
size_t BIO_ctrl_get_write_guarantee(BIO *b);
#define BIO_get_read_request(b) (int)BIO_ctrl(b,BIO_C_GET_READ_REQUEST,0,NULL)
size_t BIO_ctrl_get_read_request(BIO *b);
int BIO_ctrl_reset_read_request(BIO *b);
可以看到,这些函数中大多数是宏定义函数并且都是基于BIO_ctrl函数的。
BIO对类型的BIO是一对source/sink型的BIO,数据通常是从一个BIO缓冲写入,从另一个BIO读出。其实,从源代码(bss_bio.c)可以看出,所谓的BIO对只是将两个BIO的终端输出(BIO结构中参数peer的ptr成员)相互设置为对方,从而形成一种对称的结构,如下:
bio1->peer->ptr=bio2
bio2->peer->ptr=bio1
数据流向1(写bio1,读bio2):--->bio1--->bio2--->
数据流行2(写bio2,读bio1):--->bio2--->bio1--->
因为没有提供内部数据结构的内存锁结构(lock),所以,一般来说这个BIO对的两个BIO都必须在一个线程下使用。因为BIO链通常是以一个source/sink BIO结束的,所以就可以实现应用程序通过控制BIO对的一个BIO从而控制整个BIO链的数据处理。其实,也就相当于BIO对给应用程序提供了一个处理整个BIO链的入口。上次我们说BIO对的时候就说过,BIO对的一个典型应用就是在应用程序里面控制TLS/SSL的I/O接口,一般来说,在应用程序想在TLS/SSL中使用非标准的传输方法或者不适合使用标准的socket方法的时候就可以采用这样的方法来实现。
前面提过,BIO对释放的时候,需要分别释放两个BIO,如果在使用BIO_free或者BIO_free_all释放了其中一个BIO的时候,另一个BIO就也必须要释放。
当BIO对使用在双向应用程序的时候,如TLS/SSL,一定要对写缓冲区里面的数据执行flush操作。当然,也可以通过在BIO对中的另一个BIO调用BIO_pending函数,如果有数据在缓冲区中,那么就将它们读出并发送到底层的传输通道中区。为了使请求或BIO_should_read函数调用成功(为true),在执行任何正常的操作(如select)之前,都必须这样做才行。
下面举一个例子说明执行flush操作的重要性:
考虑在TLS/SSL握手过程中,采用了BIO_write函数发送了数据,相应的操作应该使BIO_read。BIO_write操作成功执行并将数据写入到写缓冲区中。BIO_read调用开始会失败,BIO_should_retry返回true。如果此时对写缓冲区不执行flush操作,那么BIO_read调用永远不会成功,因为底层传输通道会一直等待直到数据有效(但数据却在写缓冲区里,没有传到底层通道)。
【BIO_s_bio】
该函数返回一个BIO对类型的BIO_METHOD,其定义如下:
static BIO_METHOD methods_biop =
{ BIO_TYPE_BIO,
"BIO pair",
bio_write,
bio_read,
bio_puts,
NULL /* 没有定义 bio_gets */,
bio_ctrl,
bio_new,
bio_free,
NULL /* 没有定义 bio_callback_ctrl */
};
从定义中可以看到,该类型的BIO不支持BIO_gets的功能。
BIO_read函数从缓冲BIO中读取数据,如果没有数据,则发出一个重试请求。
BIO_write函数往缓冲BIO中写入数据,如果缓冲区已满,则发出一个重试请求。
BIO_ctrl_pending和BIO_ctrl_wpending函数可以用来查看在读或写缓冲区里面有效的数据的数量。
BIO_reset函数将写缓冲区里面的数据清除。
【BIO_make_bio_pair】
该函数将两个单独的BIO连接起来成为一个BIO对。
【BIO_destroy_pair】
该函数跟上面的函数相反,它将两个连接起来的BIO对拆开;如果一个BIO对中的任何一个BIO被释放,该操作会自动执行。
【BIO_shutdown_wr】
该函数关闭BIO对的其中一个BIO,一个BIO被关闭后,针对该BIO的任何写操作都会返回错误。从另一个BIO读数据的时候要么返回剩余的有效数据,要么返回EOF。
【BIO_set_write_buf_size】
该函数设置BIO的缓冲区大小。如果该BIO的缓存区大小没有初始化,那么就会使用默认的值,大小为17k,这对于一个TLS记录来说是足够大的了。
【BIO_get_write_buf_size】
该函数返回写缓冲区的大小。
【BIO_new_bio_pair】
该函数我们在前面的《BIO系列之9---BIO对的创建和应用》中已经做了详细的介绍,其实,它是调用了BIO_new,BIO_make_bio_pair和BIO_set_write_buf_size函数来创建一对BIO对的。如果两个缓冲区长度的参数都为零,那么就会使用默认的缓冲区长度。
【BIO_get_write_guarantee和BIO_ctrl_get_write_guarantee】
这两个函数返回当前能够写入BIO的数据的最大长度。如果往BIO写入的数据长度比该函数返回的数据长度大,那么BIO_write返回的写入数据长度会小于要求写入的数据,如果缓冲区已经满了,则会发出一个重试的请求。这两个函数的唯一不同之处是一个使用函数实现的,一个是使用宏定义实现的。
【BIO_get_read_request和BIO_ctrl_get_read_request】
这两个函数返回要求发送的数据的长度,这通常是在对该BIO对的另一个BIO执行读操作时因为缓冲区数据为空导致失败时发出的请求。所以,这通常用来表明现在应该写入多少数据才能使接下来的读操作能够成功执行,这在TLS/SSL应用程序中是非常有用的,因为对于这个协议来说,读取的数据长度比缓冲区的数据长度通常要有意义的多。如果在读操作成功之后调用这两个函数会返回0,如果在调用该函数之前有新的数据写入(不管是部分还是全部满足需要读取的数据的要求),那么调用该函数也会返回0。理所当然,该函数返回的数据长度肯定不会大于BIO_get_write_guarantee函数返回的数据长度。
【BIO_ctrl_reset_read_request】
该函数就是把BIO_get_read_request要返回值设置为0。
【参考文档】
《BIO系列之9---BIO对的创建和应用》
---根据openssl doc/crypto/bio_s_bio.pod翻译和自己的理解写成
(作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之openssl专业论坛)
前面我们已经介绍过BIO对的概念,其实更进一步,BIO对也是作为一种source/sink类型的BIO来处理的,也就是说,BIO里面还提供了一种专门的BIO_METHO方法来处理BIO对的各种操作。BIO对类型的BIO各种相关的函数定义如下(openssl/bio.h):
BIO_METHOD *BIO_s_bio(void);
#define BIO_make_bio_pair(b1,b2) (int)BIO_ctrl(b1,BIO_C_MAKE_BIO_PAIR,0,b2)
#define BIO_destroy_bio_pair(b) (int)BIO_ctrl(b,BIO_C_DESTROY_BIO_PAIR,0,NULL)
#define BIO_shutdown_wr(b) (int)BIO_ctrl(b, BIO_C_SHUTDOWN_WR, 0, NULL)
#define BIO_set_write_buf_size(b,size) (int)BIO_ctrl(b,BIO_C_SET_WRITE_BUF_SIZE,size,NULL)
#define BIO_get_write_buf_size(b,size) (size_t)BIO_ctrl(b,BIO_C_GET_WRITE_BUF_SIZE,size,NULL)
int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2);
#define BIO_get_write_guarantee(b) (int)BIO_ctrl(b,BIO_C_GET_WRITE_GUARANTEE,0,NULL)
size_t BIO_ctrl_get_write_guarantee(BIO *b);
#define BIO_get_read_request(b) (int)BIO_ctrl(b,BIO_C_GET_READ_REQUEST,0,NULL)
size_t BIO_ctrl_get_read_request(BIO *b);
int BIO_ctrl_reset_read_request(BIO *b);
可以看到,这些函数中大多数是宏定义函数并且都是基于BIO_ctrl函数的。
BIO对类型的BIO是一对source/sink型的BIO,数据通常是从一个BIO缓冲写入,从另一个BIO读出。其实,从源代码(bss_bio.c)可以看出,所谓的BIO对只是将两个BIO的终端输出(BIO结构中参数peer的ptr成员)相互设置为对方,从而形成一种对称的结构,如下:
bio1->peer->ptr=bio2
bio2->peer->ptr=bio1
数据流向1(写bio1,读bio2):--->bio1--->bio2--->
数据流行2(写bio2,读bio1):--->bio2--->bio1--->
因为没有提供内部数据结构的内存锁结构(lock),所以,一般来说这个BIO对的两个BIO都必须在一个线程下使用。因为BIO链通常是以一个source/sink BIO结束的,所以就可以实现应用程序通过控制BIO对的一个BIO从而控制整个BIO链的数据处理。其实,也就相当于BIO对给应用程序提供了一个处理整个BIO链的入口。上次我们说BIO对的时候就说过,BIO对的一个典型应用就是在应用程序里面控制TLS/SSL的I/O接口,一般来说,在应用程序想在TLS/SSL中使用非标准的传输方法或者不适合使用标准的socket方法的时候就可以采用这样的方法来实现。
前面提过,BIO对释放的时候,需要分别释放两个BIO,如果在使用BIO_free或者BIO_free_all释放了其中一个BIO的时候,另一个BIO就也必须要释放。
当BIO对使用在双向应用程序的时候,如TLS/SSL,一定要对写缓冲区里面的数据执行flush操作。当然,也可以通过在BIO对中的另一个BIO调用BIO_pending函数,如果有数据在缓冲区中,那么就将它们读出并发送到底层的传输通道中区。为了使请求或BIO_should_read函数调用成功(为true),在执行任何正常的操作(如select)之前,都必须这样做才行。
下面举一个例子说明执行flush操作的重要性:
考虑在TLS/SSL握手过程中,采用了BIO_write函数发送了数据,相应的操作应该使BIO_read。BIO_write操作成功执行并将数据写入到写缓冲区中。BIO_read调用开始会失败,BIO_should_retry返回true。如果此时对写缓冲区不执行flush操作,那么BIO_read调用永远不会成功,因为底层传输通道会一直等待直到数据有效(但数据却在写缓冲区里,没有传到底层通道)。
【BIO_s_bio】
该函数返回一个BIO对类型的BIO_METHOD,其定义如下:
static BIO_METHOD methods_biop =
{ BIO_TYPE_BIO,
"BIO pair",
bio_write,
bio_read,
bio_puts,
NULL /* 没有定义 bio_gets */,
bio_ctrl,
bio_new,
bio_free,
NULL /* 没有定义 bio_callback_ctrl */
};
从定义中可以看到,该类型的BIO不支持BIO_gets的功能。
BIO_read函数从缓冲BIO中读取数据,如果没有数据,则发出一个重试请求。
BIO_write函数往缓冲BIO中写入数据,如果缓冲区已满,则发出一个重试请求。
BIO_ctrl_pending和BIO_ctrl_wpending函数可以用来查看在读或写缓冲区里面有效的数据的数量。
BIO_reset函数将写缓冲区里面的数据清除。
【BIO_make_bio_pair】
该函数将两个单独的BIO连接起来成为一个BIO对。
【BIO_destroy_pair】
该函数跟上面的函数相反,它将两个连接起来的BIO对拆开;如果一个BIO对中的任何一个BIO被释放,该操作会自动执行。
【BIO_shutdown_wr】
该函数关闭BIO对的其中一个BIO,一个BIO被关闭后,针对该BIO的任何写操作都会返回错误。从另一个BIO读数据的时候要么返回剩余的有效数据,要么返回EOF。
【BIO_set_write_buf_size】
该函数设置BIO的缓冲区大小。如果该BIO的缓存区大小没有初始化,那么就会使用默认的值,大小为17k,这对于一个TLS记录来说是足够大的了。
【BIO_get_write_buf_size】
该函数返回写缓冲区的大小。
【BIO_new_bio_pair】
该函数我们在前面的《BIO系列之9---BIO对的创建和应用》中已经做了详细的介绍,其实,它是调用了BIO_new,BIO_make_bio_pair和BIO_set_write_buf_size函数来创建一对BIO对的。如果两个缓冲区长度的参数都为零,那么就会使用默认的缓冲区长度。
【BIO_get_write_guarantee和BIO_ctrl_get_write_guarantee】
这两个函数返回当前能够写入BIO的数据的最大长度。如果往BIO写入的数据长度比该函数返回的数据长度大,那么BIO_write返回的写入数据长度会小于要求写入的数据,如果缓冲区已经满了,则会发出一个重试的请求。这两个函数的唯一不同之处是一个使用函数实现的,一个是使用宏定义实现的。
【BIO_get_read_request和BIO_ctrl_get_read_request】
这两个函数返回要求发送的数据的长度,这通常是在对该BIO对的另一个BIO执行读操作时因为缓冲区数据为空导致失败时发出的请求。所以,这通常用来表明现在应该写入多少数据才能使接下来的读操作能够成功执行,这在TLS/SSL应用程序中是非常有用的,因为对于这个协议来说,读取的数据长度比缓冲区的数据长度通常要有意义的多。如果在读操作成功之后调用这两个函数会返回0,如果在调用该函数之前有新的数据写入(不管是部分还是全部满足需要读取的数据的要求),那么调用该函数也会返回0。理所当然,该函数返回的数据长度肯定不会大于BIO_get_write_guarantee函数返回的数据长度。
【BIO_ctrl_reset_read_request】
该函数就是把BIO_get_read_request要返回值设置为0。
【参考文档】
《BIO系列之9---BIO对的创建和应用》