【DM版本】:DM8
【操作系统】:centos 7
【CPU】: x86_64
【问题描述】*:
源码如下:
#ifdef _WIN32
#include <windows.h>
#define msleep(microsecond) Sleep(microsecond) // ms
#else
#include <unistd.h>
#define msleep(microsecond) usleep(1000 * microsecond) // ms
#endif
#include <oci.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
OCIError *errhp;
OCIEnv *envhp;
OCICPool *poolhp;
OraText *poolName;
sb4 poolNameLen;
CONST OraText *database = (OraText *)"192.168.1.1";
CONST OraText *username = (OraText *)"SYSDBA";
CONST OraText *password = (OraText *)"SYSDBA";
/* Local functions */
void checkerr(OCIError *errhp, sword status);
void queryRows();
int main()
{
sword lstat;
OCIEnvCreate(&envhp, OCI_THREADED | OCI_OBJECT, (dvoid *)0, NULL, NULL, NULL, 0, (dvoid **)0);
(void)OCIHandleAlloc((dvoid *)envhp, (dvoid **)&errhp, OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0);
(void)OCIHandleAlloc((dvoid *)envhp, (dvoid **)&poolhp, OCI_HTYPE_CPOOL, (size_t)0, (dvoid **)0);
/* CREATE THE CONNECTION POOL */
if (lstat = OCIConnectionPoolCreate(envhp,errhp, poolhp, &poolName, &poolNameLen,
database, (sb4)strlen((const signed char *)database),
0, 1, 1,
username, (sb4)strlen((const signed char *)username),
password, (sb4)strlen((const signed char *)password),
OCI_DEFAULT))
{
checkerr(errhp, lstat);
return; // exit(1);
}
// OCI_ATTR_CONN_TIMEOUT
// ub4 timeout = 5;
// checkerr(errhp, OCIAttrSet(poolhp, OCI_HTYPE_CPOOL, (dvoid *)&timeout, (ub4)sizeof(timeout), OCI_ATTR_CONN_TIMEOUT, errhp));
while (1)
{
// 最大连接数为1,正常应该只执行一次
queryRows();
msleep(1000);
}
checkerr(errhp, OCIConnectionPoolDestroy(poolhp, errhp, OCI_DEFAULT));
checkerr(errhp, OCIHandleFree((dvoid *)poolhp, OCI_HTYPE_CPOOL));
checkerr(errhp, OCIHandleFree((dvoid *)errhp, OCI_HTYPE_ERROR));
return 0;
}
void queryRows()
{
OCIDefine *defhp[200];
text selectst1[256];
ub4 session_count = 0;
sword status = 0;
sword lstat = 0;
OCISvcCtx *svchp = (OCISvcCtx *)0;
OCIError *errhp2 = (OCIError *)0;
OCIHandleAlloc((dvoid *)envhp, (dvoid **)&errhp2, OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0);
/* Logon in Connection Pool mode */
if (lstat = OCILogon2(envhp, errhp2, &svchp,
(CONST OraText *)username, (ub4)strlen((const signed char *)username),
(CONST OraText *)password, (ub4)strlen((const signed char *)password),
(CONST OraText *)poolName, (ub4)poolNameLen,
OCI_CPOOL))
{
printf("xx OCILogon2 lstat: %d\n", lstat);
checkerr(errhp2, lstat);
return; // exit(1);
}
printf("OCILogon2 lstat: %d\n", lstat);
sprintf(selectst1, "select count(*) from v$sessions;");
OCIStmt *stmthp = (OCIStmt *)0;
OCIHandleAlloc(envhp, (dvoid **)&stmthp, OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);
checkerr(errhp2, OCIStmtPrepare(stmthp, errhp2, (text *)selectst1,
(ub4)strlen((const signed char *)selectst1), OCI_NTV_SYNTAX,
OCI_DEFAULT));
checkerr(errhp2, OCIDefineByPos(stmthp, &defhp[0], errhp2, (ub4)1,
(dvoid *)&session_count, (sb4)sizeof(ub4), (ub2)SQLT_INT, (dvoid *)0,
(ub2 *)0, (ub2 *)0, OCI_DEFAULT));
if (lstat = OCIStmtExecute(svchp, stmthp, errhp2, (ub4)0,
(ub4)0, (OCISnapshot *)0, (OCISnapshot *)0, OCI_DEFAULT))
{
checkerr(errhp2, lstat);
return; // exit(1);
}
status = OCIStmtFetch(stmthp, errhp2, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
while (status != OCI_NO_DATA)
{
printf("session_count:%u \n", session_count);
status = OCIStmtFetch(stmthp, errhp2, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
}
checkerr(errhp2, OCIHandleFree((dvoid *)stmthp, OCI_HTYPE_STMT));
// OCILogoff之后OCILogon2一直返回0
// 不调用OCILogoff,OCILogon2断网返回-1
checkerr(errhp2, OCILogoff((dvoid *)svchp, errhp2));
}
void checkerr(OCIError *errhp, sword status)
{
text errbuf[512];
sb4 errcode = 0;
switch (status)
{
case OCI_SUCCESS:
break;
case OCI_SUCCESS_WITH_INFO:
(void)printf("Error - OCI_SUCCESS_WITH_INFO\n");
break;
case OCI_NEED_DATA:
(void)printf("Error - OCI_NEED_DATA\n");
break;
case OCI_NO_DATA:
(void)printf("Error - OCI_NODATA\n");
break;
case OCI_ERROR:
(void)OCIErrorGet((dvoid *)errhp, (ub4)1, (text *)NULL, &errcode,
errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
(void)printf("Error - %d, %.*s\n", errcode, 512, errbuf);
break;
case OCI_INVALID_HANDLE:
(void)printf("Error - OCI_INVALID_HANDLE\n");
break;
case OCI_STILL_EXECUTING:
(void)printf("Error - OCI_STILL_EXECUTE\n");
break;
case OCI_CONTINUE:
(void)printf("Error - OCI_CONTINUE\n");
break;
default:
(void)printf("Error - unknown error, status: %d\n", status);
break;
}
}
问题1补充:
使用OCI方式的连接池,设置最大最小连接数无效(最大设置为1,但是queryRows的循环可以执行超过一次)
测试时,注释掉queryRows()函数中的
checkerr(errhp2, OCILogoff((dvoid *)svchp, errhp2));
也就是,使其占用会话不释放。
正常情况,由于OCIConnectionPoolCreate设置了最大连接数为1,因此,第二次执行queryRows()应该报错才对。
$ ./dm_test
envhp: 0x4040e0, errhp: 0x4040e8, poolhp: 0x404100
Error - 24328, 非法属性
Connection Pool Created OK
OCILogon2 lstat: 0
session_count:1
OCILogon2 lstat: 0
session_count:2
OCILogon2 lstat: 0
session_count:3
问题2补充:
拔掉网线后,OCILogon2仍然返回成功,无法检测到断网,导致OCIStmtExecute函数阻塞(尝试设置OCI_ATTR_CONN_TIMEOUT提示非法属性)
网络正常的情况下,启动程序,然后拔掉网线,最后接上网线。
问题:虽然可以检测断网,但是由于没有OCILogoff,会导致会话数不断增加
$ ./dm_test
Connection Pool Created OK
OCILogon2 lstat: 0
session_count:1
OCILogon2 lstat: 0
session_count:2
OCILogon2 lstat: 0
session_count:3
OCILogon2 lstat: 0
session_count:4
OCILogon2 lstat: 0
session_count:5
xx OCILogon2 lstat: -1
Error - 3113, 创建SOCKET连接失败
xx OCILogon2 lstat: -1
Error - 3113, 创建SOCKET连接失败
xx OCILogon2 lstat: -1
Error - 3113, 创建SOCKET连接失败
xx OCILogon2 lstat: -1
Error - 3113, 创建SOCKET连接失败
xx OCILogon2 lstat: -1
Error - 3113, 创建SOCKET连接失败
OCILogon2 lstat: 0
session_count:6
OCILogon2 lstat: 0
session_count:7
OCILogon2 lstat: 0
session_count:8
网络正常的情况下,启动程序,然后拔掉网线,最后接上网线。
问题:断网时,OCILogon2依旧返回0,且OCIStmtExecute函数会阻塞,再接上网线,OCIStmtExecute函数可以恢复继续执行。
期待结果:正常使用OCILogon2和OCILogoff的情况下,可以在断网时立即返回错误(这样不会导致OCIStmtExecute阻塞)
$ ./dm_test
Connection Pool Created OK
OCILogon2 lstat: 0
session_count:1
OCILogon2 lstat: 0
session_count:1
OCILogon2 lstat: 0
session_count:1
OCILogon2 lstat: 0
session_count:1
OCILogon2 lstat: 0
// OCIStmtExecute阻塞
你这个循环应该是一直用的一个连接,没有创建新的。如果断网还返回了结果,那是断网之前就已经完成了