- 代码来源于 》》》》》》》》》》》》》》》》》》》》》 Windows Sockets网络开发VC++ 这本书
- 在stdafx.h 中添加 #pragma comment(lib,"ws2_32.lib")
//funtiondec.h
#ifndef FUNTIONDEC_H
#define FUNTIONDEC_H
#include "winsock2.h"
#define SERVER_SETUP_FAIL 1 //启动服务器失败
#define SERVERPORT 5556 //服务器TCP端口
#define TIMEFOR_THREAD_EXIT 5000 //主线程睡眠时间
#define TIMEFOR_THREAD_SLEEP 500 //等待客户端请求线程睡眠时间
#define TIMEFOR_THREAD_CLIENT 500 //线程睡眠
#define TIMEFOR_THREAD_HELP 500
#define MAX_NUM_BUF 48 //缓冲区的最大长度
#define EXPRESSION 'E' //算数表达式
#define BYEBYE 'B' //消息byebye,Byebye,OK
#define HEADERLEN (sizeof(hdr)) //头长度
void InitMember(void);
BOOL InitSocket(void);
BOOL StartServer(void);
BOOL CreateHelperAndAcceptThread(void);
void StopService(void);
void ExitServer(void);
DWORD __stdcall HelperThread(void* pParam);
DWORD __stdcall AcceptThread(void* pParam);
//数据包头结构,该结构在win32下为4byte
typedef struct _head
{
char type; //类型
unsigned short len; //数据包的长度(包括头的长度)
}hdr,*phdr;
//数据包中的数据结构
typedef struct _data
{
char buf[MAX_NUM_BUF]; //数据
}DATABUF,*pDataBuf;
/*
数据包的操作
*/
class CClient
{
public :
CClient(const SOCKET sClient, const sockaddr_in &addrClient);
virtual ~CClient();
public:
BOOL StartRuning(void); //创建发送和接收数据线程
void HandleData(const char* pExpr); //计算表达式
BOOL IsConning(void)
{
return m_bConning;
}
void DisConning(void) //断开与客户端的连接
{
m_bConning = FALSE;
}
BOOL IsExit(void) //接收和发送线程是否已经退出
{
return m_bExit;
}
char* Getm_addr()
{
return inet_ntoa(m_addr.sin_addr);;
}
public:
static DWORD __stdcall RecvDataThread(void* pParam); //接收客户端数据
static DWORD __stdcall SendDataThread(void* pParam); //向客户端发送数据
private:
CClient();
private:
SOCKET m_socket; //套接字
sockaddr_in m_addr; //地址
DATABUF m_data; //数据
HANDLE m_hEvent; //事件对象
HANDLE m_hThreadSend; //发送数据线程句柄
HANDLE m_hThreadRecv; //接收数据线程句柄
CRITICAL_SECTION m_cs; //临界区对象
BOOL m_bConning; //客户端连接状态
BOOL m_bExit; //线程退出
};
#endif
//CClient.cpp
#include "stdafx.h"
#include "funtiondec.h"
//构造函数
CClient::CClient(const SOCKET sClient, const sockaddr_in &addrClient)
{
//初始化变量
m_hThreadRecv = NULL;
m_hThreadSend = NULL;
m_socket = sClient;
m_addr = addrClient;
m_bConning = FALSE;
m_bExit = FALSE;
memset(m_data.buf, 0, MAX_NUM_BUF);
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //手动设置信号状态 ,初始化为无信号状态
InitializeCriticalSection(&m_cs); //初始化临界区
}
//析构函数
CClient::~CClient()
{
closesocket(m_socket); //关闭套接字
m_socket = INVALID_SOCKET; //套机诶无效
DeleteCriticalSection(&m_cs); //释放临界区对象
CloseHandle(m_hEvent); //释放事件对象
}
/*
创建发送和接收数据线程
*/
BOOL CClient::StartRuning(void)
{
m_bConning = TRUE; //设置连接状态
//创建接收数据线程
unsigned long ulThreadId;
m_hThreadRecv = CreateThread(NULL, 0, RecvDataThread, this, 0, &ulThreadId);
if (NULL == m_hThreadRecv)
{
return FALSE;
}
else
{
CloseHandle(m_hThreadRecv);
}
//创建接收数据线程
m_hThreadSend = CreateThread(NULL, 0, SendDataThread, this, 0, &ulThreadId);
if (NULL == m_hThreadSend)
{
return FALSE;
}
else
{
CloseHandle(m_hThreadSend);
}
return TRUE;
}
/*
接收客户端数据
*/
DWORD CClient::RecvDataThread(void* pParam)
{
CClient *pClient = (CClient*)pParam; //客户端对象指针
int reVal; //返回值
char temp[MAX_NUM_BUF]; //临时变量
memset(temp, 0, MAX_NUM_BUF);
for (; pClient->m_bConning;) //连接状态
{
reVal = recv(pClient->m_socket, temp, MAX_NUM_BUF, 0); //接收数据
//处理错误返回值
if (SOCKET_ERROR == reVal)
{
int nErrCode = WSAGetLastError();
if (WSAEWOULDBLOCK == nErrCode) //接受数据缓冲区不可用
{
continue; //继续循环
}
else if (WSAENETDOWN == nErrCode //客户端关闭了连接
|| WSAETIMEDOUT == nErrCode
|| WSAECONNRESET == nErrCode)
{
break; //线程退出
}
}
//客户端关闭了连接
if (reVal == 0)
{
break;
}
//收到数据
if (reVal > HEADERLEN)
{
pClient->HandleData(temp); //处理数据
SetEvent(pClient->m_hEvent); //通知发送数据线程
memset(temp, 0, MAX_NUM_BUF); //清空临时变量
}
Sleep(TIMEFOR_THREAD_CLIENT); //线程睡眠
}
pClient->m_bConning = FALSE; //与客户端的连接断开
SetEvent(pClient->m_hEvent); //通知发送数据线程退出
return 0; //退出
}
/*
计算表达式,打包数据
*/
void CClient::HandleData(const char* pExpr)
{
memset(m_data.buf, 0, MAX_NUM_BUF);//清空m_data
//如果"byebye" 或者 "Byebye"
if (BYEBYE == ((phdr)pExpr)->type)
{
EnterCriticalSection(&m_cs);
phdr pHeaderSend = (phdr)m_data.buf; //发送的数据
pHeaderSend->type = BYEBYE; //单词类型
pHeaderSend->len = HEADERLEN + strlen("OK"); //数据包长度
memcpy(m_data.buf + HEADERLEN, "OK", strlen("OK")); //复制数据到m_data
LeaveCriticalSection(&m_cs);
}
else
{//算数表达式
int nFirNum; //第一个数字
int nSecNum; //第二个数字
char cOper; //算数运算符
int nResult; //计算结果
//格式化读入数据
sscanf_s(pExpr + HEADERLEN, "%d%c%d", &nFirNum, &cOper,&nSecNum);
sscanf_s(pExpr + HEADERLEN, "%*d %*c %d", &nSecNum);
//计算
switch (cOper)
{
case '+':
{
nResult = nFirNum + nSecNum;
break;
}
case '-':
{
nResult = nFirNum - nSecNum;
break;
}
case '*':
{
nResult = nFirNum * nSecNum;
break;
}
case '/':
{
if (0 == nSecNum) //无效的数字
{
nResult = -1;
}
else
nResult = nFirNum / nSecNum;
break;
}
default:
nResult = -1; //无效的操作符
break;
}
//将算数表达式和计算的结果写入字符数组中
char temp[MAX_NUM_BUF];
char cEqu = '=';
sprintf_s(temp, "%d%c%d%c%d", nFirNum, cOper, nSecNum, cEqu, nResult);
//打包数据
EnterCriticalSection(&m_cs);
phdr pHeaderSend = (phdr)m_data.buf; //发送的数据
pHeaderSend->type = EXPRESSION; //数据类型为算数表达式
pHeaderSend->len = HEADERLEN + strlen(temp); //数据包的长度
memcpy(m_data.buf + HEADERLEN, temp, strlen(temp)); //复制数据到m_data
LeaveCriticalSection(&m_cs);
}
}
/*
向客户端发送数据
*/
DWORD CClient::SendDataThread(void* pParam)
{
CClient *pClient = (CClient*)pParam; //转化数据类型为CClient指针
for (; pClient->m_bConning;) //连接状态
{
//收到事件通知
if (WAIT_OBJECT_0 == WaitForSingleObject(pClient->m_hEvent, INFINITE))
{
//当客户端的连接断开时,接收数据线程先退出,然后该线程后退出,并设置退出标志
if (!pClient->m_bConning)
{
pClient->m_bExit = TRUE;
break;
}
//进入临界区
EnterCriticalSection(&pClient->m_cs);
//发送数据
phdr pHeader = (phdr)pClient->m_data.buf;
int nSendlen = pHeader->len;
int val = send(pClient->m_socket, pClient->m_data.buf, nSendlen, 0);
//处理返回错误
if (SOCKET_ERROR == val)
{
int nErrCode = WSAGetLastError();
if (nErrCode == WSAEWOULDBLOCK) //发送数据缓冲区不可用
{
continue;
}
else if (WSAENETDOWN == nErrCode
|| WSAETIMEDOUT == nErrCode
|| WSAECONNRESET == nErrCode) //客户端关闭了连接
{
//离开临界区
LeaveCriticalSection(&pClient->m_cs);
pClient->m_bConning = FALSE; //连接断开
pClient->m_bExit = TRUE; //线程退出
break;
}
else
{
//离开临界区
LeaveCriticalSection(&pClient->m_cs);
pClient->m_bConning = FALSE; //连接断开
pClient->m_bExit = TRUE; //线程结束
break;
}
}
//成功发送数据
printf_s("Send()发送数据成功\n");
//离开临界区
LeaveCriticalSection(&pClient->m_cs);
//设置事件为无信号状态
ResetEvent(&pClient->m_hEvent);
}
}
return 0;
}
// ServerCounting.cpp : 远程算数 服务器
//
#include "stdafx.h"
#include "winsock2.h"
#include "funtiondec.h" //所需函数声明
#include <list>
#include <string>
using namespace std;
HANDLE hThreadAccept; //接受客户端连接线程句柄
HANDLE hThreadHelp; //释放资源线程句柄
SOCKET sServer; //监听套接字
BOOL bServerRunning; //服务器的工作状态
HANDLE hServerEvent; //服务器退出事件对象
std::list<CClient*> clientlist; //管理连接的链表
CRITICAL_SECTION csClientList; //保护链表的临界区对象
/*
初始化全局变量
*/
void InitMember(void)
{
InitializeCriticalSection(&csClientList); //初始化临界区 临界区对象在使用之前必须初始化,在使用完后删除
hServerEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //手动设置事件,初始化为无信息状态
hThreadAccept = NULL;
hThreadHelp = 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);
if (0 != reVal)
{
printf("Can not find a usable Windows Sockets dll!");
return FALSE;
}
//创建套接字
sServer = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sServer)
return FALSE;
//设置套接字非阻塞模式
unsigned long u1 = 1;
reVal = ioctlsocket(sServer, FIONBIO, (unsigned long*)&u1);
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 StartServer(void)
{
BOOL reVal = TRUE; // 返回值
printf("输入s 或 S\n"); //提示用户输入
char cInput; //输入字符
do
{
scanf_s("%c", &cInput);
if ('s' == cInput || 'S' == cInput)
{
if (CreateHelperAndAcceptThread()) //创建清理资源和接受客户端请求的线程
{
//ShowServerStartMsg(TRUE); //创建线程成功
printf("创建线程成功\n");
}
else
{
reVal = FALSE;
}
break; //跳出循环体
}
else
{
printf("输入正确!\n");
}
} while (cInput != 's' && cInput != 'S'); //必须输入's'或者'S'字符
return reVal;
}
/*
创建释放资源线程和接收客户端请求线程
*/
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 StopService(void)
{
BOOL reVal = TRUE; //返回值
printf("输入e 或 E\n");
char cInput;
for (; bServerRunning;)
{
scanf_s("%c", &cInput);
if (cInput == 'E' || cInput == 'e')
{
if (IDOK == MessageBox(NULL, _T("Are you sure?"),
_T("Server"), MB_OKCANCEL)) //等待用户确认
break;
else
{
Sleep(TIMEFOR_THREAD_EXIT); //线程睡眠
}
}
else
{
Sleep(TIMEFOR_THREAD_EXIT); //线程睡眠
}
}
bServerRunning = FALSE; //服务器退出
printf("服务器退出!\n");
Sleep(TIMEFOR_THREAD_EXIT); //给其他线程时间退出
WaitForSingleObject(hServerEvent, INFINITE);//等待清理资源线程发送的事件
return;
}
/*
释放资源
*/
void ExitServer(void)
{
DeleteCriticalSection(&csClientList); //释放临界区对象
CloseHandle(hServerEvent); //释放事件对象句柄
closesocket(sServer); //关闭SOCKET
WSACleanup(); //卸载Windowss Sockets DLL
}
/*
接受客户端请求
*/
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 //接受客户端的请求
{
printf_s("accept请求成功\n");
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); //进入临界区
//清理已经断开的连接客户端内存空间
list<CClient*>::iterator iter = clientlist.begin();
for (iter; iter != clientlist.end();)
{
CClient *pClient = (CClient*)*iter;
if (pClient->IsExit()) //客户端线程已经退出
{
printf_s("客户端退出IP:%s\n", pClient->Getm_addr());
clientlist.erase(iter++); //删除节点
delete pClient; //释放内存
pClient = NULL;
}
else
{
iter++; //指针下移
}
}
LeaveCriticalSection(&csClientList); //离开临界区
Sleep(TIMEFOR_THREAD_HELP);
}
//服务器停止工作
if (!bServerRunning)
{
//断开每个连接,线程退出
EnterCriticalSection(&csClientList);
list<CClient*>::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;
}
int _tmain(int argc, _TCHAR* argv[])
{
//初始化全局变量
InitMember();
//初始化服务器
if (!InitSocket())
{
printf("初始化服务器失败\n");
ExitServer();
return SERVER_SETUP_FAIL;
}
//启动服务
if (!StartServer())
{
printf("启动服务器失败\n");
ExitServer();
return SERVER_SETUP_FAIL;
}
printf("stop\n");
//停止服务
StopService();
//服务器退出
ExitServer();
return 0;
}