清理资源线程用于清理为每个接收的客户端分配的资源。由HelperThread()函数实现。该函数工作于服务器两种状态下。
1.服务器正在运行
在一个for循环中,将管理客户端连接clientlist链表的节点依次取出,然后调用CClient类的IsExit()函数。如果返回值为TRUE,说明该客户端的连接已经关闭,将该节点从clientlist链表中删除,并释放客户端连接实例占用的内存空间。
2.服务器即将退出
服务器退出时,必须保证在主线程退出之前,所以子线程全自动然退出。当用户输入字符“e”或者“E”时,在StopService()函数中,修改bServerRunning变量值为FALSE。然后,调用WaitForSingleObject()函数,等待清理资源线程发送的事件通知。
接受客户端请求线程,判断bServerRunning变量值为FALSE,该线程退出。
清理资源线程,负责通知所有与客户端连接的线程自动退出。执行过程执行如下。
(1)从前面所述的for循环体中跳出。
(2)在第二个for循环体中,从clientlist链表依次取出连接客户端实例节点。调用DisConning()函数,将处于连接状态的客户端断开,使与该客户端连接的数据接收和发送线程退出。
(3)在第三个for循环体中,释放所有为CClient类实例分配的内存空间。
(4)调用SetEvent()函数,设置hServerEvent事件对象为有信号状态,通知主线程可以退出。HelperThread()线程函数的程序清单如下。
/**
* 清理资源
*/
DWORD __stdcall HelperThread(void* pParam)
{
for (;bServerRunning;) //服务器正在运行
{
EnterCriticalSection(&csClientList); //进入临界区
//清理已经断开的连接客户端内存空间
CLIENTLIST::iterator iter = clientlist.begin();
for (iter; iter != clientlist.end();)
{
CClient *pClient = (CClient*)*iter;
if (pClient->IsExit()) //客户端线程已经退出
{
clientlist.erase(iter++); //删除节点
delete pClient; //释放内存
pClient = NULL;
}else{
iter++; //指针下移
}
}
LeaveCriticalSection(&csClientList); //离开临界区
Sleep(TIMEFOR_THREAD_HELP);
}
//服务器停止工作
if (!bServerRunning)
{
//断开每个连接,线程退出
EnterCriticalSection(&csClientList);
CLIENTLIST::iterator iter = clientlist.begin();
for (iter; iter != clientlist.end();)
{
CClient *pClient = (CClient*)*iter;
//如果客户端的连接还存在,则断开连接,线程退出
if (pClient->IsConning())
{
pClient->DisConning();
}
++iter;
}
//离开临界区
LeaveCriticalSection(&csClientList);
//给连接客户端线程时间,使其自动退出
Sleep(TIMEFOR_THREAD_SLEEP);
//进入临界区
EnterCriticalSection(&csClientList);
//确保为每个客户端分配的内存空间都回收。
//如果不加入while这层循环,可能存在这样的情况。当pClient->IsExit()时,该线程还没有退出。
//那么就需要从链表的开始部分重新判断。
while ( 0 != clientlist.size())
{
iter = clientlist.begin();
for (iter; iter != clientlist.end();)
{
CClient *pClient = (CClient*)*iter;
if (pClient->IsExit()) //客户端线程已经退出
{
clientlist.erase(iter++); //删除节点
delete pClient; //释放内存空间
pClient = NULL;
}else{
iter++; //指针下移
}
}
//给连接客户端线程时间,使其自动退出
Sleep(TIMEFOR_THREAD_SLEEP);
}
LeaveCriticalSection(&csClientList); //离开临界区
}
clientlist.clear(); //清空链表
SetEvent(hServerEvent); //通知主线程退出
return 0;
}