服务端代码仅四百余行,主要是AcceptThread与HelperThread两个线程函数难以理解,全部代码如下:
#include "stdafx.h"
#include <iostream.h>
#include <afx.h>
#include <winbase.h>
#include <winsock2.h>
#include <process.h>
#include <list>
#include "Client.h"
/*
* 变量的第一个字母为小写且该字母表明该变量的类型,如 nErrCode.为整形的错误代码
* 函数的第一个字母为大写且表明函数的功能。如void AcceptThread(void).为接受连接的线程
*/
#pragma comment(lib, "ws2_32.lib")
using namespace std;
typedef list<CClient*> CLIENTLIST; //链表
#define SERVERPORT 5556 //服务器TCP端口
#define SERVER_SETUP_FAIL 1 //启动服务器失败
#define TIMEFOR_THREAD_EXIT 5000 //主线程睡眠时间
#define TIMEFOR_THREAD_HELP 1500 //清理资源线程退出时间
#define TIMEFOR_THREAD_SLEEP 500 //等待客户端请求线程睡眠时间
HANDLE hThreadAccept; //接受客户端连接线程句柄
HANDLE hThreadHelp; //释放资源线程句柄
SOCKET sServer; //监听套接字
BOOL bServerRunning; //服务器的工作状态
HANDLE hServerEvent; //关闭服务器事件对象
CLIENTLIST clientlist; //管理连接的链表
CRITICAL_SECTION csClientList; //保护链表的临界区对象
BOOL InitSever(void); //初始化
BOOL StartService(void); //启动服务
void StopService(void); //停止服务
BOOL CreateHelperAndAcceptThread(void); //创建接收客户端连接线程
void ExitServer(void); //服务器退出
void InitMember(void); //初始化全局变量
BOOL InitSocket(void); //初始化SOCKET
void ShowTipMsg(BOOL bFirstInput); //显示提示信息
void ShowServerStartMsg(BOOL bSuc); //显示服务器已经启动
void ShowServerExitMsg(void); //显示服务器正在退出
DWORD __stdcall HelperThread(void *pParam); //释放资源
DWORD __stdcall AcceptThread(void *pParam); //接受客户端连接线程
void ShowConnectNum(); //显示客户端的连接数目
int main(int argc, char* argv[])
{
//初始化服务器
if (!InitSever())
{
ExitServer();
return SERVER_SETUP_FAIL;
}
//启动服务
if (!StartService())
{
ShowServerStartMsg(FALSE);
ExitServer();
return SERVER_SETUP_FAIL;
}
//停止服务
StopService();
//服务器退出
ExitServer();
return 0;
}
/**
* 初始化
*/
BOOL InitSever(void)
{
InitMember();//初始化全局变量
//初始化SOCKET
if (!InitSocket())
return FALSE;
return TRUE;
}
/**
* 初始化全局变量
*/
void InitMember(void)
{
InitializeCriticalSection(&csClientList); //初始化临界区
hServerEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //手动设置事件,初始化为无信息号状态
hThreadAccept = NULL; //设置为NULL
hThreadHelp = NULL; //设置为NULL
sServer = INVALID_SOCKET; //设置为无效的套接字
bServerRunning = FALSE; //服务器为没有运行状态
clientlist.clear(); //清空链表
}
/**
* 初始化SOCKET
*/
BOOL InitSocket(void)
{
//返回值
int reVal;
//初始化Windows Sockets DLL
WSADATA wsData;
reVal = WSAStartup(MAKEWORD(2,2),&wsData);
//创建套接字
sServer = socket(AF_INET, SOCK_STREAM, 0);
if(INVALID_SOCKET== sServer)
return FALSE;
//设置套接字非阻塞模式
unsigned long ul = 1;
reVal = ioctlsocket(sServer, FIONBIO, (unsigned long*)&ul);
if (SOCKET_ERROR == reVal)
return FALSE;
//绑定套接字
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(SERVERPORT);
serAddr.sin_addr.S_un.S_addr = INADDR_ANY;
reVal = bind(sServer, (struct sockaddr*)&serAddr, sizeof(serAddr));
if(SOCKET_ERROR == reVal )
return FALSE;
//监听
reVal = listen(sServer, SOMAXCONN);
if(SOCKET_ERROR == reVal)
return FALSE;
return TRUE;
}
/**
* 启动服务
*/
BOOL StartService(void)
{
BOOL reVal = TRUE; //返回值
ShowTipMsg(TRUE); //提示用户输入
char cInput; //输入字符
do
{
cin >> cInput;
if ('s' == cInput || 'S' == cInput)
{
if (CreateHelperAndAcceptThread()) //创建清理资源和接受客户端请求的线程
{
ShowServerStartMsg(TRUE); //创建线程成功信息
}else{
reVal = FALSE;
}
break;//跳出循环体
}else{
ShowTipMsg(TRUE);
}
} while(cInput != 's' &&//必须输入's'或者'S'字符
cInput != 'S');
return reVal;
}
/**
* 停止服务
*/
void StopService(void)
{
BOOL reVal = TRUE; //返回值
ShowTipMsg(FALSE); //提示用户输入
char cInput; //输入的操作字符
for (;bServerRunning;)
{
cin >> cInput;
if (cInput == 'E' || cInput == 'e')
{
if (IDOK == MessageBox(NULL, "Are you sure?", //等待用户确认退出的消息框
"Server", MB_OKCANCEL))
{
break;//跳出循环体
}else{
Sleep(TIMEFOR_THREAD_EXIT); //线程睡眠
}
}else{
Sleep(TIMEFOR_THREAD_EXIT); //线程睡眠
}
}
bServerRunning = FALSE; //服务器退出
ShowServerExitMsg(); //显示服务器退出信息
Sleep(TIMEFOR_THREAD_EXIT); //给其他线程时间退出
WaitForSingleObject(hServerEvent, INFINITE);//等待清理资源线程发送的事件
return;
}
/**
* 显示提示信息
*/
void ShowTipMsg(BOOL bFirstInput)
{
if (bFirstInput)//第一次
{
cout << endl;
cout << endl;
cout << "**********************" << endl;
cout << "* *" << endl;
cout << "* s(S): Start server *" << endl;
cout << "* *" << endl;
cout << "**********************" << endl;
cout << "Please input:" << endl;
}else{//退出服务器
cout << endl;
cout << endl;
cout << "**********************" << endl;
cout << "* *" << endl;
cout << "* e(E): Exit server *" << endl;
cout << "* *" << endl;
cout << "**********************" << endl;
cout << " Please input:" << endl;
}
}
/**
* 释放资源
*/
void ExitServer(void)
{
DeleteCriticalSection(&csClientList); //释放临界区对象
CloseHandle(hServerEvent); //释放事件对象句柄
closesocket(sServer); //关闭SOCKET
WSACleanup(); //卸载Windows Sockets DLL
}
/**
* 创建释放资源线程和接收客户端请求线程
*/
BOOL CreateHelperAndAcceptThread(void)
{
bServerRunning = TRUE;//设置服务器为运行状态
//创建释放资源线程
unsigned long ulThreadId;
hThreadHelp = CreateThread(NULL, 0, HelperThread, NULL, 0, &ulThreadId);
if( NULL == hThreadHelp)
{
bServerRunning = FALSE;
return FALSE;
}else{
CloseHandle(hThreadHelp);
}
//创建接收客户端请求线程
hThreadAccept = CreateThread(NULL, 0, AcceptThread, NULL, 0, &ulThreadId);
if( NULL == hThreadAccept)
{
bServerRunning = FALSE;
return FALSE;
}else{
CloseHandle(hThreadAccept);
}
return TRUE;
}
/**
* 显示启动服务器成功与失败消息
*/
void ShowServerStartMsg(BOOL bSuc)
{
if (bSuc)
{
cout << "**********************" << endl;
cout << "* *" << endl;
cout << "* Server succeeded! *" << endl;
cout << "* *" << endl;
cout << "**********************" << endl;
}else{
cout << "**********************" << endl;
cout << "* *" << endl;
cout << "* Server failed ! *" << endl;
cout << "* *" << endl;
cout << "**********************" << endl;
}
}
/**
* 显示退出服务器消息
*/
void ShowServerExitMsg(void)
{
cout << "**********************" << endl;
cout << "* *" << endl;
cout << "* Server exit... *" << endl;
cout << "* *" << endl;
cout << "**********************" << endl;
}
/**
* 接受客户端连接
*/
DWORD __stdcall AcceptThread(void* pParam)
{
SOCKET sAccept; //接受客户端连接的套接字
sockaddr_in addrClient; //客户端SOCKET地址
for (;bServerRunning;) //服务器的状态
{
memset(&addrClient, 0, sizeof(sockaddr_in)); //初始化
int lenClient = sizeof(sockaddr_in); //地址长度
sAccept = accept(sServer, (sockaddr*)&addrClient, &lenClient); //接受客户请求
if(INVALID_SOCKET == sAccept )
{
int nErrCode = WSAGetLastError();
if(nErrCode == WSAEWOULDBLOCK) //无法立即完成一个非阻挡性套接字操作
{
Sleep(TIMEFOR_THREAD_SLEEP);
continue;//继续等待
}else {
return 0;//线程退出
}
}
else//接受客户端的请求
{
CClient *pClient = new CClient(sAccept,addrClient); //创建客户端对象
EnterCriticalSection(&csClientList); //进入在临界区
clientlist.push_back(pClient); //加入链表
LeaveCriticalSection(&csClientList); //离开临界区
pClient->StartRuning(); //为接受的客户端建立接收数据和发送数据线程
}
}
return 0;//线程退出
}
/**
* 清理资源
*/
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;
}