在OpenSSL中一共有两种类型的BIO,一种是源/目的类型的,另一种是过滤类型的,其实可以统一到一种类型,那就是统一都是过滤类型,这种说法的前提是一个古老的概念,早在unix时代,人们通常将程序看做一个过滤器,简单的给它一个输入就会得到一个输出,具体会得到什么输出就看程序员的意了,那个时候,程序没有现在如此庞大,也没有如此之多的智能和行为逻辑,就是简单的过滤功能,unix提出的一切皆文件的伟大思想就是在这个古老而又淳朴的概念之上提出来的,unix将设备抽象成文件,将显示器和键盘抽象成文件,按照程序即过滤器的思想是十分合理的,程序的输入和输出两端都连接一个文件,数据从文件中来经过过滤器到文件中去,显示器是文件,磁盘是文件,键盘也是文件,甚至内存也是文件,想想标准输入和标准输出的概念就会很容易理解了,程序是过滤器,被过滤的东西的载体就是文件,unix靠这个思想而稳定地运行到了今天。如此一来,考虑源/目的类型的BIO,其实也是一种过滤类型的BIO,所谓过滤器就是一个输入加上过滤逻辑得到一个输出,源/目的类型的BIO的输入就是源端,过滤逻辑就是直通,而输出就是目的端,过滤逻辑不在乎输出结构有没有使用者以及使用者是谁,它只要将输出放到一个地方就可以。理解是可以按照统一的方式去理解,但是事实上openssl还是区分了两种类型的BIO,在过滤类型的BIO数据结构中,一般都提供了一个缓冲区,比如加密/解密BIO,对于write,就是加密数据,然后放到缓冲区中,就到此为止了吗?不,因为过滤数据只不过是IO的一个阶段而已,openssl的BIO通达信交易接口周大田,接口提供了BIO链机制,并且规定一个过滤类型的BIO必须是BIO链的一个中间环节,这就是说,最终必须将这些数据写入一个源/目的的BIO,因为在openssl中的BIO机制中,只有源/目的类型的BIO才是真正的数据载体,而过滤类型的BIO仅仅提供数据缓冲区作为中间数据载体,这在下面的代码体现明显:
staticintbuffer_write(BIO*b,constchar*in,intin
{
inti,num=0;
BIO_F_BUFFER_CTX*ctx;
if((in==NUL||(inl<=0))return(0);
ctx=(BIO_F_BUFFER_CTX*)b->ptr;
if((ctx==NUL||(b->next_bio==NUL)return(0);
BIO_clear_retry_flags(;
start:
i=ctx->obuf_size-(ctx->obuf_len+ctx->obuf_of;
if(i>=in//缓冲区不满的情况下,就将数据加入缓冲区之后然后返回,只有在缓冲区满了或者手动刷新的时候才刷新缓冲区
{
memcpy(&(ctx->obuf[ctx->obuf_len]),in,in;
ctx->obuf_len+=inl;
return(num+in;
}
if(ctx->obuf_len!=0)//加入当前的数据后刷新缓冲区
{
if(i>0)/*letsfillitupifwecan*/
{
memcpy(&(ctx->obuf[ctx->obuf_len]),in,;
...//偏移处理
ctx->obuf_len+=i;
}
for(;;)//循环刷新,直到终了或者出错
{//调用BIO_write通达信交易接口周大田,接口函数,注意传入的BIO是当前BIO链的下一个BIO,数据就是缓冲区内的数据
i=BIO_write(b->next_bio,&(ctx->obuf[ctx->obuf_off]),ctx->obuf_le;
if(i<=0)
...//结束或者出错处理以及偏移处理
}
...//不考虑的情况
}
以上是buffer类型的BIO的一个write回调函数,buffer类型的BIO是一种很简单的过滤类型的BIO,仅仅提供两个缓冲区用于缓冲数据,别的什么也不做,在buffer-BIO的上层,用户调用BIO_write写入数据,写到哪里了呢?事实上是缓存到buffer的输出缓冲区里面了,等到缓冲区满了的时候再将其刷入更底层的BIO,这个更底层的BIO可能仍然是一个过滤类型的BIO,也可能是一个源/目的类型的BIO,但是BIO链的最终肯定是一个源/目的类型的BIO而不能是一个过滤类型的BIO或者是NUILL,因此在此回调函数的最后刷新缓冲区的时候还是用了BIO_write通达信交易接口周大田,接口函数进行数据写入,最终肯定是写入了一个源/目的类型的BIO代表的数据载体,比如套接字,文件,甚至内存。
更为复杂的加密解密BIO复杂在处理细节,处理逻辑和上面的buffer-BIO是一样的,只不过在写入下层的BIO之前将数据加密了,在读取的时候,从下层BIO读到数据之后先解密然后再传递给更为上层的BIO。在openssl中,控制逻辑十分简单,BIO_write通达信交易接口周大田,接口函数可以被一切可以控制BIO的实体使用,比如最终的用户或者BIO本身,因为BIO是链式的结构,因此整个过程在函数上体现了一个递归的过程,自己控制自己,于这个过程不同的是有些程序的逻辑大量使用了MVC架构,其中抽象出一个控制器,这种方式固然不错,但是我个人感觉openssl的方式看起来更加美丽。有的时候这种递归的控制方式十分有效,它的特点在于将控制器集成到了回调函数本身,你难道在深感此方式难懂的同时不觉得它也很灵活吗?
BIO的架构很简单,基本就是一个结构体和一个回调函数集合:
structbio_st
{
BIO_METHOD*method;//BIO方法结构,是决定BIO类型和行为的重要参数,各种BIO的不同之处主要也正在于此项。
long(*callbac(structbio_st*,int,constchar*,int,long,lon;//一个可选的回调函数,用户可以更好的进行控制
char*cb_arg;//回调函数的参数
intinit;//是否已经初始化标志
intshutdown;//BIO是否已经打开
intflags;
intretry_reason;
intnum;
void*ptr;//私有数据指针,对于不同的BIO类型有着不同的解释,比如对于一个加密解密的BIO,它就是一个BIO_ENC_CTX
structbio_st*next_bio;
structbio_st*prev_bio;
intreferences;
unsignedlongnum_read;//读出的数据长度
unsignedlongnum_write;//写入的数据长度
CRYPTO_EX_DATAex_data;
};
仍然以buffer-BIO为例,methods_buffer就是它的重要的回调函数集合:
staticBIO_METHODmethods_buffer=
{
BIO_TYPE_BUFFER,
'buffer',
buffer_write,
buffer_read,
buffer_puts,
buffer_gets,
buffer_ctrl,
buffer_new,
buffer_free,
buffer_callback_ctrl,
};
BIO_METHOD*BIO_f_buffer(voi
{
return(&methods_buffer);
}
BIO机制提供BIO_write通达信交易接口周大田,接口函数,该通达信交易接口周大田,接口函数的实现就是调用不同BIO的回调函数集合中的write函数,对于这些回调函数怎么实现就看用户的策略了,BIO仅仅是提供了一个总体的框架,它对内部的实现没有任何要求,正如上面说的,控制权也被集成在了回调函数里面,比如一个过滤类型的BIO的write回调函数的实现中就有BIO_write(b->next_bio,...)通达信交易接口周大田,接口函数的调用,再比如对于源/目的类型的BIO,一个connect的socket的write回调函数就是:
staticintsock_write(BIO*b,constchar*in,intin
{
intret;
clear_socket_error();
ret=writesocket(b->num,in,in;
BIO_clear_retry_flags(;
if(ret<=0)
{
if(BIO_sock_should_retry(ret))
BIO_set_retry_write(;
}
return(ret);
}
而writesocket就是send函数。可能是由于openssl最初基于unix/linux家族吧,作为一切皆文件的一种迎合,BIO提供了BIO_set_fd通达信交易接口周大田,接口函数,可以将一个文件描述符和一个BIO联系起来,当然这个通达信交易接口周大田,接口函数的参数是void*类型的,其实你可以传递任何类型的参数给它。BIO在某种意义上提供了一种关于IO的更高层次的抽象,将所有的IO操作分成了源/目的类型的和过滤类型的,其实正如前面所述,只要过滤类型就够了,毕竟所有的程序其实都是过滤器,IO操作就是过滤行为,过滤本身被抽象成IO,但是那样的话整个机制就成了一个完全的理论框架,没有一点可操作性了,openssl在统一视和可用上做了一个完美的折中,这样就可以在不缺失可操作性的前提下最大限度的提供理论的和谐,openssl提供的BIO中内置了很多的BIO,比如file类型,socket类型,buffer类型,加密解密类型,ssl类型等等,对于这些我们可以随意方便的使用。
BIO提供了BIO_ctrl通达信交易接口周大田,接口函数,使用这个函数可以对BIO进行控制,类似于标准文件操作的ioctl,类似于关联BIO和文件描述符的操作都是使用这个通达信交易接口周大田,接口函数完成的,很多的操作都会定位到这个BIO_ctrl函数,对于不同的BIO类型,其ctrl也不同,其实ctrl函数也是回调函数集合中的一个,BIO_ctrl最终会调用BIO类型特定的ctrl回调函数的。
文章为作者独立观点,不代表股票交易接口观点