技術メモ

2.0の通信モデル

04WebServer 1.xx では、ブロッキングモードのソケットを通信に使用していた。 マルチスレッド環境では、通信関数でスレッドをブロックさせればよく、selectを使う必要が無いので、転送効率は良い。

04WebServer 2.0 の通信では、以下の機能の実装を必要とした。

  • タイムアウト
  • 中断
  • 任意のタイミングでの、切断検知

同等の機能は、1.xxでも実装されていたが、別のスレッドからclosesocketを呼ぶなど、良い実装とは言えなかった為、以下のように実装を変更した。

  • タイムアウト
    • オーバーラップI/O + 待機関数
  • 中断
    • オーバーラップI/O + 待機関数
  • 任意のタイミングでの、切断検知
    • WSAEventSelectでFD_CLOSEを有効にしておく

当初、WSAEventSelectと非同期I/Oで実現していたが、転送効率が悪かったため、オーバーラップI/Oへ変更した。 これにより、OpenSSLの利用も、独自BIOを使用したものに変更した。

OpenSSL

BIO

  • BIO.ptr は、自由に使ってよさそう
  • BIO.num は、自由に使ってよさそう(ファイルディスクプリタの割り当てに使われる模様)

独自BIOは、以下のように実装

/*!
	COverlappedSocket を入出力に使うBIO

	参考:bss_file.c, bss_sock.c
*/
#include "StdAfx.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "OverlappedSocket.h"

#define BIO_TYPE_OVR_SOCKET		(30 | 0x400)	//	BIO_TYPE_SOURCE_SINK

#ifndef MS_CALLBACK
	#define MS_CALLBACK
#endif

static int MS_CALLBACK	ovrs_write(BIO *h, const char *buf, int num);
static int MS_CALLBACK	ovrs_read(BIO *h, char *buf, int size);
static int MS_CALLBACK	ovrs_puts(BIO *h, const char *str);
static long MS_CALLBACK	ovrs_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int MS_CALLBACK	ovrs_new(BIO *h);
static int MS_CALLBACK	ovrs_free(BIO *data);

static BIO_METHOD methods_ovrs =
{
	BIO_TYPE_OVR_SOCKET,
	"COverlappedSocket",
	ovrs_write,
	ovrs_read,
	ovrs_puts,
	NULL, // ovr_gets,
	ovrs_ctrl,
	ovrs_new,
	ovrs_free,
	NULL,
};

/*!
	インタフェース取得
*/
BIO_METHOD *BIO_s_ovrs(void)
{
	return(&methods_ovrs);
}

/*!
	割り当て
*/
BIO *BIO_new_ovrs(COverlappedSocket *ovrs)
{
	if(ovrs == NULL)
		return(NULL);

	//	新規生成
	BIO *ret = BIO_new(BIO_s_ovrs());
	if(ret == NULL)
		return(NULL);

	//	設定
	ret->ptr = ovrs;
	ret->init = 1;
	return(ret);
}

/*!
	初期化
*/
static int ovrs_new(BIO *bi)
{
	bi->init=0;
	bi->num=0;
	bi->ptr=NULL;
	bi->flags=0;
	return(1);
}

/*!
	開放
*/
static int ovrs_free(BIO *a)
{
	if (a == NULL)
		return(0);
	a->ptr=NULL;
	a->init=0;
	return(1);
}

/*!	
	読み込み
*/
static int ovrs_read(BIO *b, char *out, int outl)
{
	if(out != NULL)
	{
		//	ポインタ取得
		COverlappedSocket *ovrs = (COverlappedSocket*)b->ptr;

		//	操作実行
		return(ovrs->BlockingRecv(out, outl));
	}
	return(0);
}

/*!
	書き込み
*/
static int ovrs_write(BIO *b, const char *in, int inl)
{
	//	ポインタ取得
	COverlappedSocket *ovrs = (COverlappedSocket*)b->ptr;

	//	操作実行
	return(ovrs->BlockingSend(const_cast<char *>(in), inl));
}

/*!
	書き込み(文字列)
*/
static int ovrs_puts(BIO *bp, const char *str)
{
	return(ovrs_write(bp,str, strlen(str)));
}


/*!
	コントロール
*/
static long ovrs_ctrl(BIO *b, int cmd, long num, void *ptr)
{
	long ret=1;

	switch (cmd)
	{
	case BIO_CTRL_RESET:
		num=0;
	case BIO_C_FILE_SEEK:
		ret=0;
		break;

	case BIO_CTRL_DUP:
	case BIO_CTRL_FLUSH:
		ret=1;
		break;

	default:
		ret=0;
		break;
	}
	return(ret);
}