mirror of
https://gitee.com/acl-dev/acl.git
synced 2024-11-30 10:57:34 +08:00
add aio ssl samples
This commit is contained in:
parent
3315a5a9b2
commit
984ad1e206
@ -3,6 +3,11 @@
|
||||
------------------------------------------------------------------------
|
||||
513) 2015.12.4
|
||||
513.1) bugfix: fdp->stream->sys_read_ready should be check in event_prepare
|
||||
513.2) performance: acl_xmlcode.c 中的函数 acl_xml_encode/acl_xml_decode 通过
|
||||
提前预分配内存,以减少重复分配/释放内存的次数,从而在一定程度上提升内存操作性能
|
||||
513.3) bugfix: 将清楚 ACL_VSTREAM::sys_read_ready 标志位的操作从 acl_vstream.c
|
||||
的函数 sys_read 中移除,在 acl_sys_socket.c 的函数 acl_socket_read 进行清除该标志位
|
||||
|
||||
function of events.c for reading event.
|
||||
|
||||
512) 2015.12.3
|
||||
|
@ -55,13 +55,19 @@ int acl_xml_encode(const char *in, ACL_VSTRING *out)
|
||||
{
|
||||
const unsigned char *ptr = (const unsigned char*) in;
|
||||
int n = 0;
|
||||
size_t len = strlen(in);
|
||||
|
||||
len += len / 2;
|
||||
|
||||
ACL_VSTRING_SPACE(out, len);
|
||||
|
||||
while (*ptr) {
|
||||
if (__charmap[*ptr] != NULL) {
|
||||
acl_vstring_strcat(out, __charmap[*ptr]);
|
||||
n++;
|
||||
} else
|
||||
} else {
|
||||
ACL_VSTRING_ADDCH(out, *ptr);
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
@ -146,11 +152,13 @@ static const char* markup_unescape(const char* in, ACL_VSTRING* out)
|
||||
|
||||
int acl_xml_decode(const char *in, ACL_VSTRING *out)
|
||||
{
|
||||
int n = 0, len;
|
||||
int n = 0, len = (int) strlen(in);
|
||||
const char *ptr = in, *pre;
|
||||
const ACL_TOKEN *token;
|
||||
const XML_SPEC *spec;
|
||||
|
||||
ACL_VSTRING_SPACE(out, len);
|
||||
|
||||
acl_pthread_once(&__token_once, xml_decode_init);
|
||||
if (__token_tree == NULL)
|
||||
acl_msg_fatal("__token_tree null");
|
||||
|
@ -274,24 +274,14 @@ AGAIN:
|
||||
/* 清除系统错误号 */
|
||||
acl_set_error(0);
|
||||
|
||||
/* 必须在调用 fread_fn/read_fn 前清除可读标志位,这样 IO 钩子函数
|
||||
* 在重置该标志位时才能生效
|
||||
*/
|
||||
in->sys_read_ready = 0;
|
||||
|
||||
if (in->type == ACL_VSTREAM_TYPE_FILE) {
|
||||
read_cnt = in->fread_fn(ACL_VSTREAM_FILE(in), buf, size,
|
||||
in->sys_read_ready ? 0 : in->rw_timeout,
|
||||
in, in->context);
|
||||
in->rw_timeout, in, in->context);
|
||||
if (in->read_cnt > 0)
|
||||
in->sys_offset += in->read_cnt;
|
||||
} else {
|
||||
/* 如果由事件引擎设置了套接字有数据可读,则将超时时间设 0,
|
||||
* 这样可以减少一次对读超时的调用
|
||||
*/
|
||||
read_cnt = in->read_fn(ACL_VSTREAM_SOCK(in), buf, size,
|
||||
in->sys_read_ready ? 0 : in->rw_timeout,
|
||||
in, in->context);
|
||||
in->rw_timeout, in, in->context);
|
||||
}
|
||||
|
||||
if (read_cnt > 0) {
|
||||
|
@ -108,7 +108,8 @@ acl_off_t acl_lseek(ACL_FILE_HANDLE fh, acl_off_t offset, int whence)
|
||||
else if (whence == SEEK_END)
|
||||
method = FILE_END;
|
||||
else {
|
||||
acl_msg_error("%s(%d): invalid whence(%d)", myname, __LINE__, whence);
|
||||
acl_msg_error("%s(%d): invalid whence(%d)",
|
||||
myname, __LINE__, whence);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -146,8 +147,8 @@ int acl_file_write(ACL_FILE_HANDLE fh, const void *buf, size_t size,
|
||||
return nWritten;
|
||||
}
|
||||
|
||||
int acl_file_writev(ACL_FILE_HANDLE fh, const struct iovec *vector, int count,
|
||||
int timeout acl_unused, ACL_VSTREAM *fp acl_unused,
|
||||
int acl_file_writev(ACL_FILE_HANDLE fh, const struct iovec *vector,
|
||||
int count, int timeout acl_unused, ACL_VSTREAM *fp acl_unused,
|
||||
void *arg acl_unused)
|
||||
{
|
||||
int i, n;
|
||||
@ -228,7 +229,8 @@ int acl_fstat(ACL_FILE_HANDLE fh, struct acl_stat *buf)
|
||||
unsigned long ulAvail;
|
||||
int rc;
|
||||
|
||||
rc = PeekNamedPipe(fh, NULL, 0, NULL, &ulAvail, NULL);
|
||||
rc = PeekNamedPipe(fh, NULL, 0, NULL,
|
||||
&ulAvail, NULL);
|
||||
if (rc) {
|
||||
buf->st_size = (_off_t) ulAvail;
|
||||
} else {
|
||||
@ -260,8 +262,9 @@ int acl_fstat(ACL_FILE_HANDLE fh, struct acl_stat *buf)
|
||||
if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
buf->st_mode |= (_S_IREAD + (_S_IREAD >> 3) + (_S_IREAD >> 6));
|
||||
else
|
||||
buf->st_mode |= ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3)
|
||||
+ ((_S_IREAD|_S_IWRITE) >> 6));
|
||||
buf->st_mode |= ((_S_IREAD|_S_IWRITE)
|
||||
+ ((_S_IREAD|_S_IWRITE) >> 3)
|
||||
+ ((_S_IREAD|_S_IWRITE) >> 6));
|
||||
|
||||
/* set file date fields */
|
||||
if (!FileTimeToLocalFileTime(&(bhfi.ftLastWriteTime), &LocalFTime)
|
||||
@ -283,8 +286,8 @@ int acl_fstat(ACL_FILE_HANDLE fh, struct acl_stat *buf)
|
||||
||bhfi.ftLastAccessTime.dwHighDateTime)
|
||||
{
|
||||
|
||||
if (!FileTimeToLocalFileTime(&(bhfi.ftLastAccessTime),
|
||||
&LocalFTime )
|
||||
if (!FileTimeToLocalFileTime(
|
||||
&(bhfi.ftLastAccessTime), &LocalFTime )
|
||||
|| !FileTimeToSystemTime(&LocalFTime, &SystemTime))
|
||||
{
|
||||
retval = -1;
|
||||
@ -336,7 +339,7 @@ int acl_fstat(ACL_FILE_HANDLE fh, struct acl_stat *buf)
|
||||
|
||||
/* Common return code */
|
||||
done:
|
||||
return(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#elif defined(ACL_UNIX)
|
||||
|
@ -99,7 +99,7 @@ int acl_socket_close(ACL_SOCKET fd)
|
||||
}
|
||||
|
||||
int acl_socket_read(ACL_SOCKET fd, void *buf, size_t size,
|
||||
int timeout, ACL_VSTREAM *fp acl_unused, void *arg acl_unused)
|
||||
int timeout, ACL_VSTREAM *fp, void *arg acl_unused)
|
||||
{
|
||||
#if 0
|
||||
WSABUF wsaData;
|
||||
@ -118,6 +118,11 @@ int acl_socket_read(ACL_SOCKET fd, void *buf, size_t size,
|
||||
#else
|
||||
int ret;
|
||||
|
||||
if (fp != NULL && fp->sys_read_ready) {
|
||||
fp->sys_read_ready = 0;
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
if (timeout > 0 && acl_read_wait(fd, timeout) < 0) {
|
||||
errno = acl_last_error();
|
||||
return -1;
|
||||
@ -212,12 +217,18 @@ int acl_socket_close(ACL_SOCKET fd)
|
||||
}
|
||||
|
||||
int acl_socket_read(ACL_SOCKET fd, void *buf, size_t size,
|
||||
int timeout, ACL_VSTREAM *fp acl_unused, void *arg acl_unused)
|
||||
int timeout, ACL_VSTREAM *fp, void *arg acl_unused)
|
||||
{
|
||||
if (fp != NULL && fp->sys_read_ready) {
|
||||
fp->sys_read_ready = 0;
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
if (timeout > 0 && acl_read_wait(fd, timeout) < 0) {
|
||||
errno = acl_last_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return read(fd, buf, size);
|
||||
}
|
||||
|
||||
|
4
lib_acl_cpp/samples/ssl/aio_client/Makefile
Normal file
4
lib_acl_cpp/samples/ssl/aio_client/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
base_path = ../../..
|
||||
include ../../Makefile.in
|
||||
PROG = aio_client
|
||||
EXTLIBS += -lpolarssl -lz
|
367
lib_acl_cpp/samples/ssl/aio_client/main.cpp
Normal file
367
lib_acl_cpp/samples/ssl/aio_client/main.cpp
Normal file
@ -0,0 +1,367 @@
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include "lib_acl.h"
|
||||
#include "acl_cpp/lib_acl.hpp"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
acl::aio_handle* handle;
|
||||
char addr[64];
|
||||
int connect_timeout;
|
||||
int read_timeout;
|
||||
int nopen_limit;
|
||||
int nopen_total;
|
||||
int nwrite_limit;
|
||||
int nwrite_total;
|
||||
int nread_total;
|
||||
int id_begin;
|
||||
} IO_CTX;
|
||||
|
||||
static bool connect_server(acl::polarssl_conf* ssl_conf, IO_CTX* ctx, int id);
|
||||
|
||||
/**
|
||||
* 客户端异步连接流回调函数类
|
||||
*/
|
||||
class client_io_callback : public acl::aio_open_callback
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* 构造函数
|
||||
* @param client {aio_socket_stream*} 异步连接流
|
||||
* @param ssl_conf {acl::polarssl_conf*} 非空时指定 SSL 连接方式
|
||||
* @param ctx {IO_CTX*}
|
||||
* @param id {int} 本流的ID号
|
||||
*/
|
||||
client_io_callback(acl::aio_socket_stream* client,
|
||||
acl::polarssl_conf* ssl_conf, IO_CTX* ctx, int id)
|
||||
: client_(client)
|
||||
, ssl_conf_(ssl_conf)
|
||||
, ctx_(ctx)
|
||||
, nwrite_(0)
|
||||
, nread_(0)
|
||||
, id_(id)
|
||||
{
|
||||
}
|
||||
|
||||
~client_io_callback()
|
||||
{
|
||||
std::cout << ">>>ID: " << id_
|
||||
<< ", io_callback deleted now!" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基类虚函数, 当异步流读到所要求的数据时调用此回调函数
|
||||
* @param data {char*} 读到的数据地址
|
||||
* @param len {int} 读到的数据长度
|
||||
* @return {bool} 返回给调用者 true 表示继续,否则表示需要关闭异步流
|
||||
*/
|
||||
bool read_callback(char*, int len)
|
||||
{
|
||||
nread_ += len;
|
||||
ctx_->nread_total++;
|
||||
|
||||
std::cout << ">>>>>>>> current len: " << len
|
||||
<< "; total_len: " << nread_ << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基类虚函数, 当异步流写成功时调用此回调函数
|
||||
* @return {bool} 返回给调用者 true 表示继续,否则表示需要关闭异步流
|
||||
*/
|
||||
bool write_callback()
|
||||
{
|
||||
ctx_->nwrite_total++;
|
||||
nwrite_++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基类虚函数, 当该异步流关闭时调用此回调函数
|
||||
*/
|
||||
void close_callback()
|
||||
{
|
||||
if (client_->is_opened() == false)
|
||||
{
|
||||
std::cout << "Id: " << id_ << " connect "
|
||||
<< ctx_->addr << " error: "
|
||||
<< acl::last_serror();
|
||||
|
||||
// 如果是第一次连接就失败,则退出
|
||||
if (ctx_->nopen_total == 0)
|
||||
{
|
||||
std::cout << ", first connect error, quit";
|
||||
/* 获得异步引擎句柄,并设置为退出状态 */
|
||||
client_->get_handle().stop();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 获得异步引擎中受监控的异步流个数 */
|
||||
int nleft = client_->get_handle().length();
|
||||
if (ctx_->nopen_total == ctx_->nopen_limit && nleft == 1)
|
||||
{
|
||||
std::cout << "Id: " << id_ << " stop now! nstream: "
|
||||
<< nleft << std::endl;
|
||||
/* 获得异步引擎句柄,并设置为退出状态 */
|
||||
client_->get_handle().stop();
|
||||
}
|
||||
|
||||
// 必须在此处删除该动态分配的回调类对象以防止内存泄露
|
||||
delete this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基类虚函数,当异步流超时时调用此函数
|
||||
* @return {bool} 返回给调用者 true 表示继续,否则表示需要关闭异步流
|
||||
*/
|
||||
bool timeout_callback()
|
||||
{
|
||||
std::cout << "Connect " << ctx_->addr
|
||||
<< " Timeout ..." << std::endl;
|
||||
client_->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read_wakeup()
|
||||
{
|
||||
// 取得之前通过 setup_hook 注册的 SSL IO句柄
|
||||
acl::polarssl_io* hook =
|
||||
(acl::polarssl_io*) client_->get_hook();
|
||||
|
||||
if (hook == NULL)
|
||||
{
|
||||
std::cout << "get hook error"<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试进行 SSL 握手
|
||||
if (hook->handshake() == false)
|
||||
{
|
||||
logger_error("ssl handshake failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// SSL 握手还未完成,等待本函数再次被触发
|
||||
if (hook->handshake_ok() == false)
|
||||
return true;
|
||||
|
||||
// 如果 SSL 握手已经成功,则开始读数据
|
||||
|
||||
printf("ssl handshake ok\r\n");
|
||||
|
||||
// 由 reactor 模式转为 proactor 模式,从而取消
|
||||
// read_wakeup 回调过程
|
||||
client_->disable_read();
|
||||
|
||||
// 开始与服务端的读写过程
|
||||
return begin_run();
|
||||
}
|
||||
|
||||
/**
|
||||
* 基类虚函数, 当异步连接成功后调用此函数
|
||||
* @return {bool} 返回给调用者 true 表示继续,否则表示需要关闭异步流
|
||||
*/
|
||||
bool open_callback()
|
||||
{
|
||||
// 连接成功,设置IO读写回调函数
|
||||
client_->add_read_callback(this);
|
||||
client_->add_write_callback(this);
|
||||
ctx_->nopen_total++;
|
||||
|
||||
acl::assert_(id_ > 0);
|
||||
if (ctx_->nopen_total < ctx_->nopen_limit)
|
||||
{
|
||||
// 开始进行下一个连接过程
|
||||
if (connect_server(ssl_conf_, ctx_, id_ + 1) == false)
|
||||
std::cout << "connect error!" << std::endl;
|
||||
}
|
||||
|
||||
// 设置 SSL 方式
|
||||
if (ssl_conf_)
|
||||
return setup_ssl(*ssl_conf_);
|
||||
|
||||
// 开始与服务端的读写过程
|
||||
else
|
||||
return begin_run();
|
||||
}
|
||||
|
||||
private:
|
||||
acl::aio_socket_stream* client_;
|
||||
acl::polarssl_conf* ssl_conf_;
|
||||
IO_CTX* ctx_;
|
||||
int nwrite_;
|
||||
int nread_;
|
||||
int id_;
|
||||
|
||||
bool setup_ssl(acl::polarssl_conf& ssl_conf)
|
||||
{
|
||||
acl::polarssl_io* ssl =
|
||||
new acl::polarssl_io(ssl_conf, false, true);
|
||||
|
||||
// 将 SSL IO 过程注册至异步流中
|
||||
if (client_->setup_hook(ssl) == ssl)
|
||||
{
|
||||
std::cout << "open ssl error!" << std::endl;
|
||||
ssl->destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 开始 SSL 握手过程
|
||||
if (ssl->handshake() == false)
|
||||
{
|
||||
client_->remove_hook();
|
||||
ssl->destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 开始异步 SSL 握手过程,满足可读条件时将触发 read_wakeup
|
||||
client_->read_wait(10);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool begin_run(void)
|
||||
{
|
||||
// 异步向服务器发送数据
|
||||
char buf[8194];
|
||||
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[sizeof(buf) - 1] = '\n';
|
||||
buf[sizeof(buf) - 2] = '\r';
|
||||
|
||||
client_->write(buf, (int) sizeof(buf));
|
||||
|
||||
// 异步从服务器读取数据
|
||||
client_->read();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static bool connect_server(acl::polarssl_conf* ssl_conf, IO_CTX* ctx, int id)
|
||||
{
|
||||
// 开始异步连接远程服务器
|
||||
acl::aio_socket_stream* stream = acl::aio_socket_stream::open(
|
||||
ctx->handle, ctx->addr, ctx->connect_timeout);
|
||||
if (stream == NULL)
|
||||
{
|
||||
std::cout << "connect " << ctx->addr << " error!" << std::endl;
|
||||
std::cout << "stoping ..." << std::endl;
|
||||
if (id == 0)
|
||||
ctx->handle->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
acl_non_blocking(stream->sock_handle(), ACL_BLOCKING);
|
||||
|
||||
// 创建连接后的回调函数类
|
||||
client_io_callback* callback = new
|
||||
client_io_callback(stream, ssl_conf, ctx, id);
|
||||
|
||||
// 添加连接成功的回调函数类
|
||||
stream->add_open_callback(callback);
|
||||
|
||||
// 添加连接失败后回调函数类
|
||||
stream->add_close_callback(callback);
|
||||
|
||||
// 添加连接超时的回调函数类
|
||||
stream->add_timeout_callback(callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usage(const char* procname)
|
||||
{
|
||||
printf("usage: %s -h[help] -l server_addr -c nconnect"
|
||||
" -n io_max -k[use kernel event: epoll/kqueue/devpoll"
|
||||
" -t connect_timeout -S[use_ssl]\n", procname);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
bool use_kernel = false;
|
||||
int ch;
|
||||
IO_CTX ctx;
|
||||
acl::polarssl_conf* ssl_conf = NULL;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.connect_timeout = 5;
|
||||
ctx.nopen_limit = 1;
|
||||
ctx.id_begin = 1;
|
||||
ctx.nwrite_limit = 1;
|
||||
acl::safe_snprintf(ctx.addr, sizeof(ctx.addr), "127.0.0.1:9800");
|
||||
|
||||
while ((ch = getopt(argc, argv, "hc:n:kl:t:S")) > 0)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'c':
|
||||
ctx.nopen_limit = atoi(optarg);
|
||||
if (ctx.nopen_limit <= 0)
|
||||
ctx.nopen_limit = 10;
|
||||
break;
|
||||
case 'n':
|
||||
ctx.nwrite_limit = atoi(optarg);
|
||||
if (ctx.nwrite_limit <= 0)
|
||||
ctx.nwrite_limit = 10;
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
case 'k':
|
||||
use_kernel = true;
|
||||
break;
|
||||
case 'l':
|
||||
acl::safe_snprintf(ctx.addr, sizeof(ctx.addr),
|
||||
"%s", optarg);
|
||||
break;
|
||||
case 't':
|
||||
ctx.connect_timeout = atoi(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
ssl_conf = new acl::polarssl_conf();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acl::meter_time(__FUNCTION__, __LINE__, "-----BEGIN-----");
|
||||
acl::acl_cpp_init();
|
||||
acl::log::stdout_open(true);
|
||||
|
||||
acl::aio_handle handle(use_kernel ? acl::ENGINE_KERNEL : acl::ENGINE_SELECT);
|
||||
ctx.handle = &handle;
|
||||
|
||||
if (connect_server(ssl_conf, &ctx, ctx.id_begin) == false)
|
||||
{
|
||||
std::cout << "enter any key to exit." << std::endl;
|
||||
getchar();
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Connect " << ctx.addr << " ..." << std::endl;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 如果返回 false 则表示不再继续,需要退出
|
||||
if (handle.check() == false)
|
||||
break;
|
||||
}
|
||||
|
||||
acl::string buf;
|
||||
|
||||
buf << "total open: " << ctx.nopen_total
|
||||
<< ", total write: " << ctx.nwrite_total
|
||||
<< ", total read: " << ctx.nread_total;
|
||||
|
||||
acl::meter_time(__FUNCTION__, __LINE__, buf.c_str());
|
||||
|
||||
delete ssl_conf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
12
lib_acl_cpp/samples/ssl/aio_client/stdafx.h
Normal file
12
lib_acl_cpp/samples/ssl/aio_client/stdafx.h
Normal file
@ -0,0 +1,12 @@
|
||||
// stdafx.h : 标准系统包含文件的包含文件,
|
||||
// 或是常用但不常更改的项目特定的包含文件
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//#include <iostream>
|
||||
//#include <tchar.h>
|
||||
|
||||
// TODO: 在此处引用程序要求的附加头文件
|
||||
#include "acl_cpp/lib_acl.hpp"
|
3
lib_acl_cpp/samples/ssl/aio_client/valgrind.sh
Normal file
3
lib_acl_cpp/samples/ssl/aio_client/valgrind.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
valgrind --show-reachable=yes --tool=memcheck --leak-check=yes -v ./aio_client -k -S -c 1 -n 10
|
4
lib_acl_cpp/samples/ssl/aio_server/Makefile
Normal file
4
lib_acl_cpp/samples/ssl/aio_server/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
base_path = ../../..
|
||||
include ../../Makefile.in
|
||||
PROG = aio_server
|
||||
EXTLIBS += -lpolarssl -lz
|
353
lib_acl_cpp/samples/ssl/aio_server/main.cpp
Normal file
353
lib_acl_cpp/samples/ssl/aio_server/main.cpp
Normal file
@ -0,0 +1,353 @@
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include "lib_acl.h"
|
||||
#include "acl_cpp/acl_cpp_init.hpp"
|
||||
#include "acl_cpp/stdlib/log.hpp"
|
||||
#include "acl_cpp/stream/polarssl_conf.hpp"
|
||||
#include "acl_cpp/stream/polarssl_io.hpp"
|
||||
#include "acl_cpp/stream/aio_handle.hpp"
|
||||
#include "acl_cpp/stream/aio_istream.hpp"
|
||||
#include "acl_cpp/stream/aio_listen_stream.hpp"
|
||||
#include "acl_cpp/stream/aio_socket_stream.hpp"
|
||||
|
||||
static int __max = 0;
|
||||
static int __timeout = 0;
|
||||
static int __max_used = 0;
|
||||
static int __cur_used = 0;
|
||||
|
||||
// SSL 模式下的 SSL 配置对象
|
||||
static acl::polarssl_conf* __ssl_conf;
|
||||
|
||||
/**
|
||||
* 异步客户端流的回调类的子类
|
||||
*/
|
||||
class io_callback : public acl::aio_callback
|
||||
{
|
||||
public:
|
||||
io_callback(acl::aio_socket_stream* client)
|
||||
: client_(client)
|
||||
, i_(0)
|
||||
, nread_(0)
|
||||
{
|
||||
}
|
||||
|
||||
~io_callback()
|
||||
{
|
||||
printf("delete io_callback now ...\r\n");
|
||||
__cur_used++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读回调虚函数,该回调函数当满足了类 aio_istream 实例中的
|
||||
* gets/read 的可读条件后被调用,由异步框架内部将符合条件的数
|
||||
* 据读出,直接传递给用户的子类
|
||||
* @param data {char*} 读到的数据的指针地址
|
||||
* @param len {int} 读到的数据长度(> 0)
|
||||
* @return {bool} 该函数返回 false 通知异步引擎关闭该异步流
|
||||
*/
|
||||
bool read_wakeup()
|
||||
{
|
||||
acl::polarssl_io* hook = (acl::polarssl_io*) client_->get_hook();
|
||||
if (hook == NULL)
|
||||
{
|
||||
// 非 SSL 模式,异步读取数据
|
||||
client_->read(__timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尝试进行 SSL 握手
|
||||
if (hook->handshake() == false)
|
||||
{
|
||||
printf("ssl handshake failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果 SSL 握手已经成功,则开始按行读数据
|
||||
if (hook->handshake_ok())
|
||||
{
|
||||
// 由 reactor 模式转为 proactor 模式,从而取消
|
||||
// read_wakeup 回调过程
|
||||
client_->disable_read();
|
||||
|
||||
// 异步读取数据,将会回调 read_callback
|
||||
client_->read(__timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
// SSL 握手还未完成,等待本函数再次被触发
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现父类中的虚函数,客户端流的读成功回调过程
|
||||
* @param data {char*} 读到的数据地址
|
||||
* @param len {int} 读到的数据长度
|
||||
* @return {bool} 返回 true 表示继续,否则希望关闭该异步流
|
||||
*/
|
||||
bool read_callback(char* data, int len)
|
||||
{
|
||||
i_++;
|
||||
nread_ += len;
|
||||
|
||||
std::cout << ">>>read len: " << len
|
||||
<< "; total read: " << nread_ << std::endl;
|
||||
|
||||
// 如果远程客户端希望退出,则关闭之
|
||||
if (strncasecmp(data, "quit", 4) == 0)
|
||||
{
|
||||
client_->format("Bye!\r\n");
|
||||
client_->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果远程客户端希望服务端也关闭,则中止异步事件过程
|
||||
if (strncasecmp(data, "stop", 4) == 0)
|
||||
{
|
||||
client_->format("Stop now!\r\n");
|
||||
client_->close(); // 关闭远程异步流
|
||||
|
||||
// 通知异步引擎关闭循环过程
|
||||
client_->get_handle().stop();
|
||||
}
|
||||
|
||||
// 向远程客户端回写收到的数据
|
||||
|
||||
client_->write(data, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现父类中的虚函数,客户端流的写成功回调过程
|
||||
* @return {bool} 返回 true 表示继续,否则希望关闭该异步流
|
||||
*/
|
||||
bool write_callback()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现父类中的虚函数,客户端流的超时回调过程
|
||||
*/
|
||||
void close_callback()
|
||||
{
|
||||
// 必须在此处删除该动态分配的回调类对象以防止内存泄露
|
||||
delete this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现父类中的虚函数,客户端流的超时回调过程
|
||||
* @return {bool} 返回 true 表示继续,否则希望关闭该异步流
|
||||
*/
|
||||
bool timeout_callback()
|
||||
{
|
||||
std::cout << "Timeout, delete it ..." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
acl::aio_socket_stream* client_;
|
||||
int i_;
|
||||
int nread_;
|
||||
};
|
||||
|
||||
/**
|
||||
* 异步监听流的回调类的子类
|
||||
*/
|
||||
class io_accept_callback : public acl::aio_accept_callback
|
||||
{
|
||||
public:
|
||||
io_accept_callback() {}
|
||||
~io_accept_callback()
|
||||
{
|
||||
printf(">>io_accept_callback over!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 基类虚函数,当有新连接到达后调用此回调过程
|
||||
* @param client {aio_socket_stream*} 异步客户端流
|
||||
* @return {bool} 返回 true 以通知监听流继续监听
|
||||
*/
|
||||
bool accept_callback(acl::aio_socket_stream* client)
|
||||
{
|
||||
//acl_non_blocking(client->sock_handle(), ACL_BLOCKING);
|
||||
|
||||
// 创建异步客户端流的回调对象并与该异步流进行绑定
|
||||
io_callback* callback = new io_callback(client);
|
||||
|
||||
// 注册异步流的读回调过程
|
||||
client->add_read_callback(callback);
|
||||
|
||||
// 注册异步流的写回调过程
|
||||
client->add_write_callback(callback);
|
||||
|
||||
// 注册异步流的关闭回调过程
|
||||
client->add_close_callback(callback);
|
||||
|
||||
// 注册异步流的超时回调过程
|
||||
client->add_timeout_callback(callback);
|
||||
|
||||
// 当限定了行数据最大长度时
|
||||
if (__max > 0)
|
||||
client->set_buf_max(__max);
|
||||
|
||||
// SSL 模式下,等待客户端发送握手信息
|
||||
if (__ssl_conf != NULL)
|
||||
{
|
||||
// 注册 SSL IO 过程的钩子
|
||||
acl::polarssl_io* ssl = new
|
||||
acl::polarssl_io(*__ssl_conf, true, true);
|
||||
|
||||
if (client->setup_hook(ssl) == ssl)
|
||||
{
|
||||
std::cout << "setup_hook error" << std::endl;
|
||||
ssl->destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将客户端置于读监听状态以触发 read_wakeup 回调过程,
|
||||
// SSL 握手过程将在 read_wakeup 中完成
|
||||
client->read_wait(__timeout);
|
||||
}
|
||||
|
||||
// 非 SSL 模式下,从异步流读一行数据
|
||||
else
|
||||
client->read(__timeout);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static void usage(const char* procname)
|
||||
{
|
||||
printf("usage: %s -h[help]\r\n"
|
||||
" -l server_addr[ip:port, default: 127.0.0.1:9800]\r\n"
|
||||
" -L line_max_length\r\n"
|
||||
" -t timeout\r\n"
|
||||
" -n conn_used_limit\r\n"
|
||||
" -k[use kernel event: epoll/iocp/kqueue/devpool]\r\n"
|
||||
" -K ssl_key_file -C ssl_cert_file [in SSL mode]\r\n",
|
||||
procname);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// 事件引擎是否采用内核中的高效模式
|
||||
bool use_kernel = false;
|
||||
acl::string key_file, cert_file;
|
||||
acl::string addr("127.0.0.1:9800");
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "l:hkL:t:K:C:n:")) > 0)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
case 'l':
|
||||
addr = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
use_kernel = true;
|
||||
break;
|
||||
case 'L':
|
||||
__max = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
__timeout = atoi(optarg);
|
||||
break;
|
||||
case 'K':
|
||||
key_file = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
cert_file = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
__max_used = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acl::log::stdout_open(true);
|
||||
|
||||
// 当私钥及证书都存在时才采用 SSL 通信方式
|
||||
if (!key_file.empty() && !cert_file.empty())
|
||||
{
|
||||
__ssl_conf = new acl::polarssl_conf();
|
||||
|
||||
// 允许服务端的 SSL 会话缓存功能
|
||||
__ssl_conf->enable_cache(true);
|
||||
|
||||
// 添加本地服务的证书
|
||||
if (__ssl_conf->add_cert(cert_file.c_str()) == false)
|
||||
{
|
||||
delete __ssl_conf;
|
||||
__ssl_conf = NULL;
|
||||
std::cout << "add_cert error: " << cert_file.c_str()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// 添加本地服务密钥
|
||||
else if (__ssl_conf->set_key(key_file.c_str()) == false)
|
||||
{
|
||||
delete __ssl_conf;
|
||||
__ssl_conf = NULL;
|
||||
std::cout << "set_key error: " << key_file.c_str()
|
||||
<< std::endl;
|
||||
}
|
||||
else
|
||||
std::cout << "Load cert&key OK!" << std::endl;
|
||||
}
|
||||
|
||||
// 构建异步引擎类对象
|
||||
acl::aio_handle handle(use_kernel ? acl::ENGINE_KERNEL : acl::ENGINE_SELECT);
|
||||
|
||||
// 创建监听异步流
|
||||
acl::aio_listen_stream* sstream = new acl::aio_listen_stream(&handle);
|
||||
|
||||
// 初始化ACL库(尤其是在WIN32下一定要调用此函数,在UNIX平台下可不调用)
|
||||
acl::acl_cpp_init();
|
||||
|
||||
// 监听指定的地址
|
||||
if (sstream->open(addr.c_str()) == false)
|
||||
{
|
||||
std::cout << "open " << addr.c_str() << " error!" << std::endl;
|
||||
sstream->close();
|
||||
// XXX: 为了保证能关闭监听流,应在此处再 check 一下
|
||||
handle.check();
|
||||
|
||||
getchar();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 创建回调类对象,当有新连接到达时自动调用此类对象的回调过程
|
||||
io_accept_callback callback;
|
||||
sstream->add_accept_callback(&callback);
|
||||
std::cout << "Listen: " << addr.c_str() << " ok!" << std::endl;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 如果返回 false 则表示不再继续,需要退出
|
||||
if (handle.check() == false)
|
||||
{
|
||||
std::cout << "aio_server stop now ..." << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (__max_used > 0 && __cur_used >= __max_used)
|
||||
break;
|
||||
}
|
||||
|
||||
// 关闭监听流并释放流对象
|
||||
sstream->close();
|
||||
|
||||
// XXX: 为了保证能关闭监听流,应在此处再 check 一下
|
||||
handle.check();
|
||||
|
||||
// 删除 acl::polarssl_conf 动态对象
|
||||
delete __ssl_conf;
|
||||
|
||||
return 0;
|
||||
}
|
12
lib_acl_cpp/samples/ssl/aio_server/stdafx.h
Normal file
12
lib_acl_cpp/samples/ssl/aio_server/stdafx.h
Normal file
@ -0,0 +1,12 @@
|
||||
// stdafx.h : 标准系统包含文件的包含文件,
|
||||
// 或是常用但不常更改的项目特定的包含文件
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//#include <iostream>
|
||||
//#include <tchar.h>
|
||||
|
||||
// TODO: 在此处引用程序要求的附加头文件
|
||||
#include "acl_cpp/lib_acl.hpp"
|
3
lib_acl_cpp/samples/ssl/aio_server/valgrind.sh
Normal file
3
lib_acl_cpp/samples/ssl/aio_server/valgrind.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
valgrind --show-reachable=yes --tool=memcheck --leak-check=yes -v ./aio_server -K ssl_key.pem -C ssl_crt.pem
|
@ -312,9 +312,12 @@ int polarssl_io::read(void* buf, size_t len)
|
||||
}
|
||||
|
||||
// 如果 SSL 缓冲区中还有未读数据,则需要重置流可读标志位,
|
||||
// 这样可以触发 acl_vstream.c 中的系统读过程
|
||||
// 这样可以触发 acl_vstream.c 及 events.c 中的系统读过程
|
||||
if (ssl_get_bytes_avail((ssl_context*) ssl_) > 0)
|
||||
stream_->sys_read_ready = 1;
|
||||
// 否则,取消可读状态,表明 SSL 缓冲区里没有数据
|
||||
else
|
||||
stream_->sys_read_ready = 0;
|
||||
|
||||
return ret;
|
||||
#else
|
||||
@ -382,19 +385,9 @@ int polarssl_io::sock_read(void *ctx, unsigned char *buf, size_t len)
|
||||
// else: ret == 1
|
||||
}
|
||||
|
||||
// 当事件引擎设置了套接字可读的状态时,超时等待为 0 秒
|
||||
|
||||
int timeout;
|
||||
if (vs->sys_read_ready)
|
||||
timeout = 0;
|
||||
else
|
||||
timeout = vs->rw_timeout;
|
||||
|
||||
int ret = acl_socket_read(fd, buf, len, timeout, vs, NULL);
|
||||
|
||||
// 须将该标志位置 0,这样在非阻塞模式下,如果 polarssl 在重复
|
||||
// 调用 sock_read 函数时,可以在前面提前返回以免阻塞在 IO 读过程
|
||||
vs->sys_read_ready = 0;
|
||||
// acl_socket_read 内部会根据 vs->sys_read_ready 标志位决定是否需要
|
||||
// 以超时方式读数据,同时会自动清除 vs->sys_read_ready 标志位
|
||||
int ret = acl_socket_read(fd, buf, len, vs->rw_timeout, vs, NULL);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user