原帖
2003/06/01
作者/修改者
版本
修改原因
时间
闻怡洋
Alpha 0.9
首次创建文档
2003/06/01
闻怡洋
添加单纯连接池管理类说明
该类只负责保存连接,而不对连接的异常进行任何处理
2003/06/28
闻怡洋
l 添加附录中关于命名管道的长连接的实现
l 添加附录中关于Socket长连接的错误检查与注意事项
2003/06/28
相关文档:
l 网络长连接类图.vsd(UML模型)
l 网络通信开发包手册.doc
其他说明:如果有任何问题和功能上的补充请联系闻怡洋
目录
第 1 章 介绍... 4
1.1 解决的问题... 4
1.2 目的... 4
1.3 版本规划... 4
1.4 当前版本不能完成的功能... 4
1.5 阅读基础... 5
第 2 章 模型... 6
2.1 用例... 6
2.2 长连接状态图... 6
2.3 类图... 8
第 3 章 连接类参考... 9
3.1 命名空间... 9
3.2 基类... 9
3.2.1 连接类基类... 9
3.2.2 连接参数类基类... 9
3.3 TCP SOCKET的实现... 10
3.3.1 连接参数类... 10
3.3.2 连接类... 10
3.4 其他连接类的实现... 11
第 4 章 连接池类函数参考... 11
4.1 连接池模板... 11
4.1.1 模板参数... 11
4.1.2 单个连接的状态... 11
4.1.3 构造函数... 11
4.1.4 初始化... 12
4.1.5 连接控制... 12
4.1.6 显示功能... 13
第 5 章 连接池类使用参考... 13
5.1 建立并初始化对象... 13
5.2 使用... 13
5.3 发现错误连接并重新连接... 14
第 6 章 连接池管理类函数参考... 14
6.1 连接池管理类模板... 14
6.1.1 模板参数... 14
6.2 连接池管理的使用... 15
第 7 章 连接池管理应用类函数参考... 15
7.1 连接池管理应用类模板... 15
7.1.1 模板参数... 15
7.1.2 构造... 15
7.1.3 初始化... 15
7.1.4 连接管理... 16
7.1.5 线程管理... 16
7.1.6 其他... 16
7.2 守护线程示范... 16
第 8 章 连接池管理应用类使用参考... 18
8.1 使用方法一... 18
8.2 使用方法二... 18
8.3 使用方法三... 18
8.4 调用顺序... 19
第 9 章 相关文件... 19
9.1 头文件... 19
9.2 库文件... 19
9.3 使用方法... 19
9.4 使用示范代码... 20
第 10 章 附录一:Socket的错误检查... 20
第 11 章 附录二:对于命名管道方式长连接的实现... 20
11.1 定义连接类和参数类... 20
11.2 定义守护线程... 21
11.3 通过模板派生新类... 21
11.4 关于命名管道错误检测... 21
第 1 章 介绍
1.1 解决的问题
由于应用开发中时常某些服务器要求使用长连接(客户端发起到服务器端的连接并一直保持连接的方式,用于减少建立连接的时间)进行数据交换,在同时存在多个网络连接时管理连接和对连接的有效性进行检查比较麻烦。
这套开发包的用途在于管理多个长连接并能够对连接的有效性实施检查和完成故障重连接。
具体说来目前版本可以在一下连接方式和通信方式下工作:
l 客户端查找一个可用的长连接并且发送数据,并在同一连接上接收数据。
l 客户端利用空闲连接发送数据后,数据并不马上通过同一连接返回,客户端需要轮询所有连接,检查返回的数据。
1.2 目的
l 减少重复开发工作量。
l 实现对多个并发的长连接的管理。
l 实现定期的连接检查功能。
l 实现自动重连接功能。
l 以模板实现,通过类重写能够支持多种网络连接方式。
1.3 版本规划
N/A
1.4 当前版本不能完成的功能
l 动态调整连接参数。
l 动态改变连接数量。
l 不能作为服务器端,等待对方的连接请求。
1.5 阅读基础
请先参考:网络通信开发包手册.doc服务器模式手册.doc 两份文档了解相关内容。
第 2 章 模型
2.1 用例
2.2 长连接状态图
enum enumSocketStatus
{
SS_ERROR=0, //线路故障
SS_FREE, //线路可用
SS_USING, //线路正在被使用
SS_CONNECTING, //线路正在被连接
SS_PICKOUT //线路被取出进行检查
};
2.3 类图
第 3 章 连接类参考
3.1 命名空间
spBase
以后的所有类均使用此命名空间。
3.2 基类
3.2.1 连接类基类
class CConnImplement //连接实现类必须实现的函数
{
public:
CConnImplement(){};
~CConnImplement(){};
virtual BOOL Connect(const CCPBase* pCP){return FALSE;};
virtual BOOL IsOK(void){return TRUE;};
virtual BOOL Close(void){return FALSE;};
};
对于每一种网络连接方式需要派生新的子类来实现连接管理。
3.2.2 连接参数类基类
class CCPBase //连接参数类必须实现的函数
{
friend CConnImplement;
public:
CCPBase(){};
~CCPBase(){};
CCPBase& operator = (const CCPBase& cp){return *this;};
};
对于每一种网络连接方式需要派生新的子类来管理连接参数。
3.3 TCP SOCKET的实现
3.3.1 连接参数类
class CSocketConnPara : public CCPBase
{
public:
CSocketConnPara():m_szLocalAddr(""),m_szRemoteAddr(""),m_iLocalPort(0),m_iRemotePort(0){};
public:
CString m_szLocalAddr,m_szRemoteAddr;
int m_iLocalPort,m_iRemotePort;
};
CSocketConnPara包含了TCP连接时的4个参数,而且必须在构造对象时提供。
3.3.2 连接类
class CSocketImplement : public CConnImplement
{
public:
CSocketImplement();
~CSocketImplement();
virtual BOOL Connect(const CSocketConnPara* pCP);
virtual BOOL IsOK(void);
virtual BOOL Close(void);
commIPC::CTCPSocket* GetSocket(void){return m_psockConn;};
protected:
commIPC::CTCPSocket *m_psockConn; //Socket连接对象
};
CSocketImplement额外提供了一个成员函数:GetSocket,用于得到成员对象m_psockConn指针。commIPC::CTCPSocket相关信息请参考:网络通信开发包手册.doc
注:关于TCP连接的错误检测的特殊性,所以成员函数IsOK比较难于脱离具体应用实现。所以在后面的章节中会具体讲述如何利用模板或者是利用守护线程重载来完成错误检查。
3.4 其他连接类的实现
建议直接通过两个基类派生,并实现所有的虚函数。(由于连接池通过模板实现所以不是强制需要通过派生的方式实现)
第 4 章 连接池类函数参考
4.1 连接池模板
4.1.1 模板参数
template <class T,class CP> class CAliveConnectionPool
其中T为连接类(从CSocketImplement派生),CP为连接参数类(从CCPBase派生)。
4.1.2 单个连接的状态
enum enumSocketStatus
{
SS_ERROR=0, //线路故障
SS_FREE, //线路可用
SS_USING, //线路正在被使用
SS_CONNECTING, //线路正在被连接
SS_PICKOUT //线路被取出进行检查
};
4.1.3 构造函数
CAliveConnectionPool(LPCSTR pszSvrName="wacp",COutputDisplay* pDis=NULL)
pszSvrName:连接池名称,系统内必须唯一。
pDis:显示类,相关内容参考:服务器模式手册.doc
4.1.4 初始化
void InitPool(int iMaxConnection)
iMaxConnection:指明最大连接数。
4.1.5 连接控制
关于连接句柄SocketID的说明:SocketID用于在一个连接池内唯一标记一个连接。在程序中利用SocketID来控制连接池中的连接(相当与句柄的作用)。
注:由于在最开始编写本类时只考虑到Socket通信方式,所以在很多地方出现Socket字样,但是通过在模板中其通信方式由具体参数化时所使用的类决定与名称无关,所以更确切的名称应该是Connection而不是Socket。后面也会多次出现这个问题,不再说明。
得到空闲的连接,同时得到SocketID。
T* GetFreeSocket(int& iSocketID)
使用完后归还连接,并设置该连接为可用。
void FreeSocket(int iSocketID)
从所有连接中找出一个错误的连接,返回是否发现错误的连接,iSocketID同时得到连接句柄
BOOL GetErrorSocket(int& iSocketID)
通过连接句柄将连接设置为错误。
void SetErrorToSocket(int iSocketID)
强制重新建立连接。
BOOL ReConnectSocket(int iSocketID,const CP* pConnPara)
得到当前现有连接使用状态的数据拷贝。
pbMap:得到连接情况数组,iMaxLen:缓冲区最大字节容量,返回的有效的状态个数
int GetSocketUseMap(CAliveConnectionPool::enumSocketStatus* peMap,int iMaxLen)
4.1.6 显示功能
void PrintfX(const char *pszFormat,...)
利用构造时保存的显示类来进行数据显示,参数格式与sprintf参数格式相同。
第 5 章 连接池类使用参考
5.1 建立并初始化对象
l 构造对象(本章内容利用Socket实现类进行讲解)
l 调用:InitPool 进行初始化
l 提供连接参数,多次调用:ReConnectSocket 建立连接
5.2 使用
l 调用:GetFreeSocket 得到空闲连接,并保存SocketID。
n 对连接进行操作后,调用:FreeSocket 归还连接。
n 如果连接发生错误,调用:SetErrorToSocket 将连接设置为错误。
示范代码:
typedef CAliveConnectionPool<CSocketImplement,CSocketConnPara> CSocketPool;
CSocketPool * pD =XXX; //假设已经创建对象
while(1)
{//循环检查
int iSocketID;
CSocketImplement* pSock = pD->GetFreeSocket(iSocketID);
if(pSock)
{
//执行操作
if(/*网络正常*/)
{//正常
pD->FreeSocket(iSocketID);
}
else
{//连接出现错误
pD->SetErrorToSocket(iSocketID);
pD->PrintfX("[%d] 检查到连接出现错误",iSocketID);
}
}
Sleep(1000);
}
5.3 发现错误连接并重新连接
l 调用:GetErrorSocket 得到错误的连接。
l 提供连接参数,重新调用:ReConnectSocket 重新建立连接
l 调用:GetSocketUseMap 得到连接池内连接的使用情况
示范代码:
typedef CAliveConnectionPool<CSocketImplement,CSocketConnPara> CSocketPool;
CSocketPool * pD =XXX; //假设已经创建对象
while(1)
{
//find error connection
// & reconnect it
int iSocketID = 0;
if(pD->GetErrorSocket(iSocketID))
{
pD->PrintfX("[%d] CSocketPool检查到连接错误,准备进行重连接",iSocketID);
CSocketConnPara cpErr = pD->GetConnectionPara(iSocketID);
BOOL fReConn = pD->ReConnectSocket(iSocketID,&cpErr);
pD->PrintfX("[%d] CSocketPoolAdmin 错误连接重连接完成 ret= %s ",iSocketID,(fReConn)?"OK":"Failed");
}
Sleep(1000);
}
第 6 章 连接池管理类函数参考
6.1 连接池管理类模板
6.1.1 模板参数
template <class T,class CP> class CAliveConnectionPoolAdmin
连接池管理类在内部维护一个连接池对象,并且进行连接管理。
基本上是在CAliveConnectionPool 的基础上添加了连接参数保存功能和守护线程管理功能。通过守护线程完成连接的状态检查和重连接。
此外对连接池对象的各种功能进行了包装。
6.2 连接池管理的使用
建议不要直接使用本类。
第 7 章 连接池管理应用类函数参考
7.1 连接池管理应用类模板
7.1.1 模板参数
template <class T,class CP,LPTHREAD_START_ROUTINE threadReConn,LPTHREAD_START_ROUTINE threadCheck>
class CSocketAliveConnectionPoolAdmin : public CAliveConnectionPoolAdmin<T,CP>
通过模板参数指定守护线程入口。
注:虽然类的名称中带有Socket字样,但是通过模板定义,该类并不只适用于Socket通信方式,在后面的章节中会演示如果通过该类实现命名管道的长连接管理。
7.1.2 构造
CSocketAliveConnectionPoolAdmin(LPCSTR pszSvrName="dfsspl",COutputDisplay* pDis=NULL,int iWaitInt=10)
iWaitInt:表示守护线程每次进行检查所休眠的秒数。
7.1.3 初始化
初始化并设置连接参数。
BOOL InitAllConnectionPara(int iMaxConn,CP* pCP);
得到指定连接参数。
CP GetConnectionPara(int iIndex)
7.1.4 连接管理
T* GetFreeSocket(int& iSocketID);
void FreeSocket(int iSocketID);
BOOL GetErrorSocket(int& iSocketID);
void SetErrorToSocket(int iSocketID);
BOOL ReConnectSocket(int iSocketID,const CP* pConnPara);
int GetSocketUseMap(CAliveConnectionPool<T,CP>::enumSocketStatus* peMap,int iMaxLen);
以上函数都是直接调用类中所管理的CAliveConnectionPool对象的相关函数。请参考CAliveConnectionPool类中对以上函数的说明。
7.1.5 线程管理
线程由模板参数中的threadReConn和threadCheck指明。
创建线程,在这个过程中成员变量m_fRunning会被修改。同时守护线程会被创建。
virtual BOOL CreateDaemonThread(void);
停止线程,在这个过程中成员变量m_fRunning会被修改。守护线程检测到m_fRunning为假后应该能够主动退出。而StopDaemonThread会等待所有的守护线程退出后再返回。
virtual BOOL StopDaemonThread(void);
7.1.6 其他
检查是否正在运行
BOOL IsRunning(void);
得到守护线程休息时间
int& GetSleepInterval(void);
7.2 守护线程示范
下面的代码演示对于TCP Socket长连接的守护线程。
typedef CSocketAliveConnectionPoolAdmin<CSocketImplement,CSocketConnPara,(LPTHREAD_START_ROUTINE)SPConnPool_ReConnectThread_Socket,(LPTHREAD_START_ROUTINE)SPConnPool_CheckConnectionThread_Socket> CSocketPoolAdmin;
//故障连接重连线程
inline DWORD SPConnPool_ReConnectThread_Socket(void* pNULL)
{
CSocketPoolAdmin* pD = (CSocketPoolAdmin* )pNULL;
while(pD->IsRunning())
{//检测是否需要退出
//find error connection
// & reconnect it
int iSocketID = 0;
if(pD->GetErrorSocket(iSocketID))
{
pD->PrintfX("[%d] CSocketPoolAdmin 检查到连接错误,准备进行重连接",iSocketID);
CSocketConnPara cpErr = pD->GetConnectionPara(iSocketID);
BOOL fReConn = pD->ReConnectSocket(iSocketID,&cpErr);
pD->PrintfX("[%d] CSocketPoolAdmin 错误连接重连接完成 ret= %s ",iSocketID,(fReConn)?"OK":"Failed");
}
Sleep(pD->GetSleepInterval()*1000);
}
return 0;
}
//检查连接是否正常的线程
inline DWORD SPConnPool_CheckConnectionThread_Socket(void* pNULL)
{
CSocketPoolAdmin* pD = (CSocketPoolAdmin* )pNULL;
while(pD->IsRunning())
{//检测是否需要退出
int iSocketID;
CSocketImplement* pSock = pD->GetFreeSocket(iSocketID);
if(pSock)
{
if(pSock->IsOK())
{//正常
pD->FreeSocket(iSocketID);
}
else
{//连接出现错误
pD->SetErrorToSocket(iSocketID);
pD->PrintfX("[%d] 检查到连接出现错误",iSocketID);
}
}
Sleep(pD->GetSleepInterval()*1000);
}
return 0;
}
第 8 章 连接池管理应用类使用参考
8.1 使用方法一
直接使用类CSocketPoolAdmin来实现Socket长连接。定义如下:
typedef CSocketAliveConnectionPoolAdmin
<CSocketImplement,CSocketConnPara,
(LPTHREAD_START_ROUTINE)SPConnPool_ReConnectThread_Socket,
(LPTHREAD_START_ROUTINE)SPConnPool_CheckConnectionThread_Socket>
CSocketPoolAdmin;
但是关于Socket的错误检测的相关内容请在附录中查找更详细的说明。
8.2 使用方法二
直接使用CSocketAliveConnectionPoolAdmin。推荐使用此种方法。
l 重新定义守护线程。守护线程可以参考上面的示范。
l 提供模板指定参数,构造CSocketAliveConnectionPoolAdmin对象。
l 调用:InitAllConnectionPara初始化连接参数。
l 调用:ReConnectSocket建立连接。
l 调用:CreateDaemonThread 启动守护线程。
l 调用:GetFreeSocket,FreeSocket,GetErrorSocket, SetErrorToSocket等函数进行连接控制和使用。
l 退出前调用StopDaemonThread,停止守护线程。
8.3 使用方法三
仿照CSocketAliveConnectionPoolAdmin重新编写连接池管理类。
参考前面代码中CSocketPoolAdmin的实现。其实本种方法就是在第二种方法上的一种具体应用。
8.4 调用顺序
l 构造对象。
l 初始化连接参数调用:InitAllConnectionPara
l 创建守护线程调用:CreateDaemonThread
l 得到空闲连接:GetFreeSocket,并在使用完后通过FreeSocket归还连接,或者通过SetErrorToSocket将连接设置为错误。
l 退出并停止守护线程调用:StopDaemonThread
l 删除对象,同时已经建立的连接会被关闭。
第 9 章 相关文件
9.1 头文件
spConnPool.h
9.2 库文件
spServer.lib 正式版本
spServer_D.lib 测试版本
9.3 使用方法
#include "spIPCComm.h"
#include "spSocketServer.h"
#include "spConnPool.h"
using namespace commIPC;
using namespace spBase;
#if defined(_DEBUG)
#pragma comment(lib, "commIPC_D.lib")
#else
#pragma comment(lib, "commIPC.lib")
#endif
#if defined(_DEBUG)
#pragma comment(lib, "spServer_D.lib")
#else
#pragma comment(lib, "spServer.lib")
#endif
9.4 使用示范代码
connPoolTest目录下connPoolTest 工程。
同一目录下的程序:server_for_test_at_9988_port.exe 作为测试用的服务器端(具有Echo功能)。
第 10 章 附录一:Socket的错误检查
参考:网络通信开发包手册.doc 文档中关于Socket错误检查的说明
所以第二种方法是推荐的方法,你在两种情况下可以将连接池中的连接设置为错误,并且由重连接守护线程进行重连接。
l 在使用连接时发现错误。
l 由检查守护线程发现错误,所以你需要尽可能重定义你的检查线程。
第 11 章 附录二:对于命名管道方式长连接的实现
11.1 定义连接类和参数类
//用于命名管道连接的连接参数类
class CPipeConnPara : public CCPBase
{
public:
CPipeConnPara():m_szPipeName(""),m_iTimeout(0){};
~CPipeConnPara(){};
CPipeConnPara& operator = (const CPipeConnPara& cp)
{
m_szPipeName=cp.m_szPipeName;
m_iTimeout=cp.m_iTimeout;
return *this;
};
public:
CString m_szPipeName;
int m_iTimeout;
};
//实现命名管道连接的长连接类
class CPipeImplement : public CConnImplement
{
public:
CPipeImplement();
~CPipeImplement();
virtual BOOL Connect(const CPipeConnPara* pCP);
virtual BOOL IsOK(void){return TRUE;};//在外部对管道状态进行检查
virtual BOOL Close(void);
commIPC::CIPCNamedPipe* GetPipe(void){return m_ppipeConn;};
protected:
commIPC::CIPCNamedPipe *m_ppipeConn; //Socket连接对象
};
11.2 定义守护线程
// Pipe连接方式,守护线程
DWORD SPConnPool_ReConnectThread_Pipe(void* pNULL);
DWORD SPConnPool_CheckConnectionThread_Pipe(void* pNULL);
11.3 通过模板派生新类
typedef CAliveConnectionPool<CPipeImplement,CPipeConnPara> CPipePool;
typedef CSocketAliveConnectionPoolAdmin
<CPipeImplement,
CPipeConnPara,
(LPTHREAD_START_ROUTINE)SPConnPool_ReConnectThread_Pipe,
(LPTHREAD_START_ROUTINE)SPConnPool_CheckConnectionThread_Pipe>
CPipePoolAdmin;
上面所有代码可以参考:spConnPool.h
11.4 关于命名管道错误检测
在实际使用时你需要参考附录一中对Socket的检查方法实现对命名管道通信错误的检查。