1.项目名称:Windows 下的网络教室
2.项目目的:实现同一局域网下服务器和客户端的通信。
3.项目描述:基于 MFC 实现了页面的布局。设置服务器实现教师与学生的通讯,用客户端模拟教 师和学生两种角色,并实现注册和登录的功能。教师端按下开始抢答后,学生端可以抢答。学生端可以进行举手、提交作业的操作,并且学生端的界面可以显示答题信息和在线好友列表。
4.项目要点:
1.使用中介者模式 观察者模式,中介者处理多个类之间的耦合性,观察者模式在接收到具体的网络消息,能够自动更新自己。
2.整个项目分为服务器和客户端,客户端又分为教师端、学生端。
3.我们要自定义数据包来识别不同的数据,这让我联想到了消息队列。
4.不同计算机结构体对齐方式不同? 序列化、反序列化。
5.因为学生的人数很多,数据类型又多,在这里我们使用数据库存储。
6.UDP广播获得好友列表,上传下载作业使用TCP。
7.怎么判断第一个学生抢到题,采用标识的方式,加锁。
8.如果是学生人数较多、但是不经常交作业,采用选择模型(Select)比较好。如果是学生人数比较多,经常要长传作业要使用完成端口模型。
5.代码部分
5.1服务器端
5.11数据库部分
MyDao.h
#pragma once
#ifndef __INCLUDE_MY_DAO_H__
#define __INCLUDE_MY_DAO_H__
#include <windows.h>
#include <list>
#include <stdio.h>
#include <tchar.h>
using namespace std;
#pragma warning(disable:4018 4996 4172)
#import "C:\\Program Files\\Common Files\\System\\ado\\msado15.dll" no_namespace rename("EOF","ADOEOF") rename("BOF","ADOBOF")
#define DATEBASE_TYPE_ACCESS 0
#define DATEBASE_TYPE_SQL2000 1
#define DATEBASE_TYPE_SQL2005 2
#define DATEBASE_TYPE_OTHER 3
#define DEF_MAX_STR_LEN (100)
class CMyDao
{
public:
CMyDao();
~CMyDao();
BOOL InitCom(); //初始化com组件
void releaseCom(); //释放com组件
public:
//初始化数据库
BOOL OpenDateBase(TCHAR* strDateBaseName,int nDataType,TCHAR* strUser,TCHAR* strPassWord,TCHAR* strIP);
//关闭数据库
void CloseDataBase();
//添加数据
BOOL AddData(TCHAR* strTableName,list<TCHAR*>& strCloumValue,int nCloumNum);
//字符串转换为指定类型
BOOL StringToDataType(TCHAR* strValue,int nDataType, VARIANT *pVar);
//获取数据
BOOL GetData(TCHAR* strSql,list<TCHAR*>& strQueryCloum,int nCloumNum,list<TCHAR*>& strDataValue);
//数据转字符串
TCHAR* DataToStringType(VARIANT Var);
//修改数据
BOOL EditData(TCHAR* strSQL, int nColumnNum, list<TCHAR*>& pStrFieldName, list<TCHAR*> &saValue);
//删除数据
BOOL DeleteData(TCHAR* strSQL);
private:
//获取指定位置的值
TCHAR* GetPos(list<TCHAR*>& str, int iPos)
{
if (str.size() <= iPos)
{
return NULL;
}
list<TCHAR*>::iterator it = str.begin();
int iCount = 0;
while (it != str.end())
{
if (iCount++ == iPos)
{
break;
}
it++;
}
return *it;
}
private:
_ConnectionPtr m_pConnection; //连接数据库的指针对象
_RecordsetPtr m_pRecordset; //操作记录集的指针对象
_CommandPtr m_pCommandmsg; //SQL命令的指针对象
BOOL m_binitComFlag;
TCHAR m_strErrorMsg[DEF_MAX_STR_LEN]; //错误信息
TCHAR m_strConnect[DEF_MAX_STR_LEN]; //连接串
};
#endif //__INCLUDE_MY_DAO_H__
MyDao.cpp
#include "stdafx.h"
#include "MyDao.h"
CMyDao::CMyDao()
{
m_binitComFlag = FALSE;
InitCom();
}
CMyDao::~CMyDao()
{
releaseCom();
}
BOOL CMyDao::InitCom()
{
#if _WIN32_WINNT > 0x500
//保留字(必须为null) ,加载方式 COINIT_MULTITHREADED多线程的方式加载
// 以多线程方式打开com通道
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
#else
::CoInitialize(NULL);
#endif
m_binitComFlag = TRUE;
return TRUE;
}
void CMyDao::releaseCom()
{
if (m_binitComFlag)
{
::CoUninitialize();
}
}
//初始化数据库
BOOL CMyDao::OpenDateBase(TCHAR* strDateBaseName,int nDataType,TCHAR* strUser,TCHAR* strPassWord,TCHAR* strIP)
{
//"colin",DATEBASE_TYPE_SQL2005,"sa","sa","127.0.0.1,2433"
if (nDataType == DATEBASE_TYPE_ACCESS)
{
//CFileFind filefind;
//BOOL bFind = filefind.FindFile(strDateBaseName);
//if (!bFind)
//{
// m_strErrorMsg = _T("找不到数据库");
// return FALSE;
//}
//else
//{
// m_strConnect.Format(_T("provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s"),strDateBaseName);
//}
}
else if (nDataType == DATEBASE_TYPE_SQL2000)
{
_stprintf(m_strConnect, _T("Driver={SQL Server};Server=%s;Uid=%s;Pwd=%s;Database=%s"), strIP,strUser,strPassWord,strDateBaseName);
//m_strConnect.Format(_T("Driver={SQL Server};Server=%s;Uid=%s;Pwd=%s;Database=%s"),strIP,strUser,strPassWord,strDateBaseName);
}
else if (nDataType == DATEBASE_TYPE_SQL2005)
{
_stprintf(m_strConnect, _T("Driver={SQL Native Client};Server=%s;Uid=%s;Pwd=%s;Database=%s"), strIP,strUser,strPassWord,strDateBaseName);
//m_strConnect.Format(_T("Driver={SQL Native Client};Server=%s;Uid=%s;Pwd=%s;Database=%s"),strIP,strUser,strPassWord,strDateBaseName);
}
else
{
_tcscpy(m_strErrorMsg, _T("没有合适的连库串"));
return FALSE;
}
//实例化指针对象
HRESULT hRes;
//把CString转换成com组件里的字符串变量
_bstr_t bstrConnection(m_strConnect);
_bstr_t bstrUser(strUser);
_bstr_t bstrPassword(strPassWord);
try
{
//创建连接对象 序列码
hRes = m_pConnection.CreateInstance(__uuidof(Connection));
if (!SUCCEEDED(hRes))
{
_tcscpy(m_strErrorMsg, _T("实例化连接指针对象失败"));
//m_strErrorMsg = _T("实例化连接指针对象失败");
return FALSE;
}
//连接数据库(连库串,用户名,密码,打开方式)
//字符串要用的是com组件的字符串 adModeShareDenyNone多用户共同可以访问一个数据库
hRes = m_pConnection->Open(bstrConnection,bstrUser,bstrPassword,adModeShareDenyNone);
if (!SUCCEEDED(hRes))
{
_tcscpy(m_strErrorMsg, _T("打开数据库失败"));
//m_strErrorMsg = _T("打开数据库失败");
return FALSE;
}
//实例化数据集指针对象
hRes = m_pRecordset.CreateInstance(__uuidof(Recordset));
if (!SUCCEEDED(hRes))
{
_tcscpy(m_strErrorMsg, _T("实例化数据集指针对象失败"));
//m_strErrorMsg = _T("实例化数据集指针对象失败");
return FALSE;
}
//实例化命令集指针对象
hRes = m_pCommandmsg.CreateInstance(__uuidof(Command));
if (!SUCCEEDED(hRes))
{
_tcscpy(m_strErrorMsg, _T("实例化命令集指针对象失败"));
//m_strErrorMsg = _T("实例化命令集指针对象失败");
return FALSE;
}
}catch(_com_error *e)
{
//AfxMessageBox(e->Description());
//m_strErrorMsg.Format(_T("%s"),e->Description());
_stprintf(m_strErrorMsg, _T("%s"), e->Description());
return FALSE;
}
return TRUE;
}
//关闭数据库
void CMyDao::CloseDataBase()
{
//TODO:
m_pConnection->Close();
//m_pConnection.DestroyInstance();
}
//添加数据
BOOL CMyDao::AddData(TCHAR* strTableName,list<TCHAR*>& strCloumValue,int nCloumNum)
{
TCHAR strQuery[100];
_stprintf(strQuery, _T("select * from [%s]"),strTableName);
//strQuery.Format(_T("select * from [%s]"),strTableName);
//自带的字符串类型
_bstr_t bstrQuery(strQuery);
_bstr_t bstrCource(m_strConnect);
HRESULT hres;
Fields *pFiles = NULL;
//ado把所有的数据类型都写在VARIANT这里面
VARIANT varIndex;
varIndex.vt = VT_I4; //R代表float CY==钱数 VT_带表的都是comADO代表的数据类型
Field *pFile = NULL;
DataTypeEnum dataType;
VARIANT varValue;
try
{
//打开表
hres = m_pRecordset->Open(bstrQuery,bstrCource,adOpenDynamic,adLockBatchOptimistic,adCmdText);
if (!SUCCEEDED(hres))
{
//m_strErrorMsg = _T("打开表失败");
_stprintf(m_strErrorMsg, _T("打开表失败"));
return FALSE;
}
else
{
//计算添加记录的行数
int nRosNum = (int)(strCloumValue.size()/nCloumNum);
if(nRosNum <= 0 )
{
//m_strErrorMsg = _T("参数个数错误");
//AfxMessageBox(_T("输入参数错误,请重新输入"));
_stprintf(m_strErrorMsg, _T("参数个数错误"));
return FALSE;
}
//添加记录 循环行
for (int i=0; i<nRosNum; i++)
{
//记录集告诉要添加记录
m_pRecordset->AddNew();
//要把记录一列一列的写进去
for (int ncloum=0;ncloum<nCloumNum;ncloum++)
{
//传入的都是字符串 我要先得到列的数据类型 然后在做相应的转换 再把数据添加到数据库里
//在数据库里列都是以集合形式存在的,所以先要得到所有的列,在从中得到每一个列
//Fields *pFiles 是个双指针
//1.得到所有的列
m_pRecordset->get_Fields(&pFiles);
//2.得到具体的列
//ADO的COM对数据库编程 除了定义了字符串其他的没有具体定义 ado把所有的数据类型都写在VARIANT这里面
varIndex.intVal = ncloum;
pFiles->get_Item(varIndex,&pFile);
//3.得到列(字段)对应的数据类型
pFile->get_Type(&dataType);
//4.转换 CString--》对应的类型 0 1 2 3 4 5 3
//TODO:
//if (StringToDataType(strCloumValue.GetAt(ncloum+i*nCloumNum),dataType,&varValue))
if (StringToDataType(GetPos(strCloumValue, ncloum+i*nCloumNum),dataType,&varValue))
{
//5.添加记录
pFile->put_Value(varValue);
if (varValue.vt == (VT_UI1|VT_ARRAY))
{
SafeArrayDestroy(varValue.parray);
}
}
}
//更新数据
m_pRecordset->UpdateBatch(adAffectCurrent);
}
//关闭记录集 如果不关闭就会被一直占用,就打不开了
//if (pStrReturnID)
//{
// m_pRecordset->MoveLast();
// m_pRecordset->get_Fields(&pFiles);
// varIndex.intVal = 0;
// pFiles->get_Item(varIndex,&pFile);
// VARIANT varKey;
// pFile->get_Value(&varKey);
// pStrReturnID->Format(_T("%d"),varKey.intVal);
//}
m_pRecordset->Close();
}
}catch(_com_error *e)
{
//AfxMessageBox(e->Description());
//m_strErrorMsg.Format(_T("%s"),e->Description());
_stprintf(m_strErrorMsg, _T("%s"),e->Description());
return FALSE;
}
return TRUE;
}
//字符串转换为指定类型
BOOL CMyDao::StringToDataType(TCHAR* strValue,int nDataType, VARIANT *pVar)
{
switch(nDataType)
{
case adInteger: //整形
pVar->vt = VT_I2;
pVar->intVal = _ttoi(strValue);
break;
case adBoolean: //BOOL类型
pVar->vt = VT_BOOL;
pVar->bVal = _ttoi(strValue);
break;
case adSingle: //单精度
pVar->vt = VT_R4;
pVar->fltVal = (float)_tstof(strValue);
break;
case adDouble: //双精度
pVar->vt = VT_R8;
pVar->dblVal = _tstof(strValue);
break;
case adBSTR: //字符串
case adChar:
case adVarChar:
case adVarWChar:
case adWChar:
case adLongVarWChar:
case adLongVarChar:
pVar->vt = VT_BSTR;
pVar->bstrVal = (bstr_t)strValue;
break;
default:
pVar->vt = VT_EMPTY;
break;
}
return TRUE;
}
//获取数据
BOOL CMyDao::GetData(TCHAR* strSql,list<TCHAR*>& strQueryCloum,int nCloumNum,list<TCHAR*>& strDataValue)
{
HRESULT het;
//_bstr_t 字符串 _variant_t 多种数据类型集合 vt类型 ..value值 //com
_bstr_t bstrSql = strSql; //执行sql
_bstr_t bstrConnect = m_strConnect; //连库串
_bstr_t bstrQueryCloum; //列名
Fields *fields;
FieldPtr fieldPtr;
TCHAR strValue[100];
_variant_t varBLOB;
try
{
het = m_pRecordset->Open(bstrSql,bstrConnect,adOpenDynamic,adLockOptimistic,adCmdText);
if (!SUCCEEDED(het))
{
//AfxMessageBox(_T("打开表失败"));
//m_strErrorMsg = _T("查询表失败");
_stprintf(m_strErrorMsg, _T("查询表失败"));
return FALSE;
}
//循环结果
list<TCHAR*>::iterator it;
//行
while(!m_pRecordset->ADOEOF)
{
// 列
it = strQueryCloum.begin();
for(int i=0;i<nCloumNum,it != strQueryCloum.end();i++,it++)
{
bstrQueryCloum = *it;
// 获得所有列
fields = m_pRecordset->GetFields();
//获得指定列
fieldPtr = fields->GetItem(bstrQueryCloum);
//获得列对应的值
varBLOB = fieldPtr->GetValue();
//long nSize =fieldPtr->ActualSize;
//注意图片名称必须为图片流的前一个字段
//strValue = DataToCStringType(varBLOB,nSize,strValue);
//strValue = DataToCStringType(varBLOB);
_stprintf(strValue, _T("%s"), DataToStringType(varBLOB));
TCHAR* pCopy = new TCHAR[_tcslen(strValue)+1];
_tcscpy(pCopy, strValue);
strDataValue.insert(strDataValue.begin(), pCopy);
//varBLOB.Detach();
}
m_pRecordset->MoveNext();
}
m_pRecordset->Close();
}
catch(_com_error *e)
{
//m_strErrorMsg = e->ErrorMessage();
//AfxMessageBox(e->ErrorMessage());
_stprintf(m_strErrorMsg, _T("%s"), e->ErrorMessage());
}
return TRUE;
}
//数据转字符串
TCHAR* CMyDao::DataToStringType(VARIANT Var)
{
TCHAR strValue[100];
switch(Var.vt)
{
case VT_BOOL:
//strValue.Format(_T("%d"), Var.boolVal);
_stprintf(strValue, _T("%d"), Var.boolVal);
break;
case VT_I4:
//strValue.Format(_T("%d"), Var.intVal);
_stprintf(strValue, _T("%d"), Var.intVal);
break;
case VT_R8:
case VT_DECIMAL:
//strValue.Format(_T("%0.2f"), Var.dblVal);
_stprintf(strValue, _T("%0.2f"), Var.dblVal);
break;
case VT_BSTR:
_stprintf(strValue, _T("%s"), Var.bstrVal);
//strValue= Var.bstrVal;
break;
default:
_stprintf(strValue, _T(""));
break;
}
return strValue;
}
//修改数据
BOOL CMyDao::EditData(TCHAR* strSQL, int nColumnNum, list<TCHAR*>& pStrFieldName, list<TCHAR*> &saValue)
{
int nIndex = 0; //列的索引
int nCount = 0; //修改的总数
int nTotal = (int)saValue.size(); //一共修改的个数
HRESULT hRet;
_variant_t varFieldName;
_variant_t varValue;
_bstr_t bstrQuery = strSQL;
_bstr_t bstrConnent = m_strConnect;
try
{
hRet = m_pRecordset->Open(bstrQuery, bstrConnent, adOpenDynamic, adLockOptimistic, adCmdText);
if(!SUCCEEDED(hRet))
{
_tcscpy(m_strErrorMsg, _T("打开表失败"));
//m_strErrorMsg = _T("打开表失败!");
return FALSE;
}
if (m_pRecordset->ADOEOF)
{
_tcscpy(m_strErrorMsg, _T("没有选择修改的行"));
//m_strErrorMsg = _T("没有选择修改的行");
m_pRecordset->Close();
return FALSE;
}
Fields *pFiles = NULL;
//ado把所有的数据类型都写在VARIANT这里面
VARIANT varIndex;
varIndex.vt = VT_I4; //R代表float CY==钱数 VT_带表的都是comADO代表的数据类型
Field *pFile = NULL;
list<TCHAR*>::iterator it = pStrFieldName.begin();
list<TCHAR*>::iterator itValue = saValue.begin();
while(!m_pRecordset->ADOEOF)
{
修改哪一列 s# sname 1 'ss' 2 'ss2' stringarr 4 2
//varFieldName.SetString((bstr_t)pStrFieldName[nIndex]);
varFieldName.SetString((bstr_t)(*it));
//varValue.SetString((bstr_t)saValue.GetAt(nCount));
varValue.SetString((bstr_t)(*itValue));
m_pRecordset->Update(varFieldName,varValue);
nIndex++;
it++;
nCount++;
itValue++;
if(nIndex>=nColumnNum)
{
nIndex = 0;
it = pStrFieldName.begin();
m_pRecordset->MoveNext();
}
if (nColumnNum == 1)
{
if(nCount == nTotal)
{
break;
}
}
else
{
if(nCount > nTotal)
{
break;
}
}
}
m_pRecordset->Close();
}
catch(_com_error *e)
{
//m_strErrorMsg = e->ErrorMessage();
//AfxMessageBox(e->Description());
_stprintf(m_strErrorMsg, _T("%s"), e->Description());
return FALSE;
}
return TRUE;
}
//删除数据
BOOL CMyDao::DeleteData(TCHAR* strSQL)
{
HRESULT hRet;
_bstr_t bstrQuery = strSQL;
_bstr_t bstrConnent = m_strConnect;
try
{
hRet = m_pRecordset->Open(bstrQuery, bstrConnent, adOpenDynamic, adLockOptimistic, adCmdText);
if(!SUCCEEDED(hRet))
{
_tcscpy(m_strErrorMsg, _T("打开表失败"));
//m_strErrorMsg = _T("打开表失败!");
return FALSE;
}
while(!m_pRecordset->ADOEOF)
{
m_pRecordset->Delete(adAffectCurrent);
m_pRecordset->MoveNext();
}
m_pRecordset->Close();
}
catch(_com_error *e)
{
//AfxMessageBox(e->Description());
_stprintf(m_strErrorMsg, _T("%s"), e->Description());
return FALSE;
}
return TRUE;
}
5.12IKernel部分
IKernel.h
#pragma once
#include "INet.h"
#include <list>
using namespace std;
class IKernel
{
public:
IKernel();
virtual ~IKernel();
public:
virtual bool Open() = 0;
virtual void Close() = 0;
virtual bool RecvData(long lRevip,char *szContent) = 0;
public:
void Attact(INet* pNet);
void Detach(INet* pNet);
void Notify(StateType ntype);
private:
list<INet*> m_lstNet;
};
IKernel.cpp
#include "stdafx.h"
#include "IKernel.h"
IKernel::IKernel()
{
}
IKernel::~IKernel()
{
m_lstNet.clear();
}
void IKernel::Attact(INet* pNet)
{
if(pNet == NULL)return;
m_lstNet.push_back(pNet);
}
void IKernel::Detach(INet* pNet)
{
list<INet*>::iterator ite = m_lstNet.begin();
while(ite != m_lstNet.end())
{
if(*ite == pNet)
{
ite = m_lstNet.erase(ite);
break;
}
ite++;
}
}
void IKernel::Notify(StateType ntype)
{
list<INet*>::iterator ite = m_lstNet.begin();
while(ite != m_lstNet.end())
{
(*ite)->Update(ntype);
ite++;
}
}
Kernel.h
#pragma once
#include "IKernel.h"
#include "UDPNet.h"
#include "MyDao.h"
class Kernel :public IKernel
{
public:
Kernel();
virtual ~Kernel();
public:
virtual bool Open();
virtual void Close();
virtual bool RecvData(long lRevip,char *szContent);
public:
void SetTeacherState(bool bTeacherState)
{
m_bTeacherState = bTeacherState;
}
bool GetTeacherState()
{
return m_bTeacherState;
}
private:
INet *m_UDPNet;
CMyDao m_ado;
bool m_bbusy;
bool m_bTeacherState;
long m_lteacherIp;
};
Kernel.cpp
#include "stdafx.h"
#include "Kernel.h"
Kernel::Kernel()
{
m_UDPNet = new UDPNet(this);
Attact(m_UDPNet);
m_bbusy = false;
m_bTeacherState = false;
}
Kernel::~Kernel()
{
if(m_UDPNet)
{
delete m_UDPNet;
m_UDPNet = NULL;
}
}
bool Kernel::Open()
{
//连接数据库
m_ado.OpenDateBase(L"ElectronicResponder",DATEBASE_TYPE_SQL2005,L"sa",L"sa",L".");
//通知启动服务器
Notify(SY_BEGIN);
return true;
}
void Kernel::Close()
{
//通知结束服务器
Notify(SY_END);
}
bool Kernel::RecvData(long lRevip,char *szContent)
{
if(lRevip == INet::GetValidIp())
{
return true;
}
int *ptype = (int*)szContent;
switch(*ptype)
{
case PT_REGISTER_RQ: //注册信息包 --去数据库中比较数据,如果不存在此人,则加入数据库 ,如果存在,返回失败
{
STRU_USERINFO *psu = ( STRU_USERINFO *)szContent;
list<TCHAR*> lstValue;
TCHAR szname[_DEFAULTNUM]= {0};
TCHAR szPassword[_DEFAULTNUM]= {0};
TCHAR szRole[_DEFAULTNUM]= {0};
STRU_USERINFO_RS su;
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,psu->m_szUserName,-1,szname,_DEFAULTNUM);
MultiByteToWideChar(CP_ACP,0,psu->m_szPassWord,-1,szPassword,_DEFAULTNUM);
MultiByteToWideChar(CP_ACP,0,psu->m_szRole,-1,szRole,_DEFAULTNUM);
#else
strcpy_s(szname,_DEFAULTNUM,psu->m_szUserName);
strcpy_s(szPassword,_DEFAULTNUM,psu->m_szPassWord);
strcpy_s(szRole,_DEFAULTNUM,psu->m_szRole);
#endif
lstValue.push_back(_T(""));
lstValue.push_back(szname);
lstValue.push_back(szPassword);
lstValue.push_back(szRole);
if(m_ado.AddData(_T("UserInfo"),lstValue,4))
{
su.m_bflag = 1;
}
else
{
su.m_bflag = 0;
}
//已经注册成功
m_UDPNet->SendData(lRevip,(char*)&su,sizeof(su));
//m_ado.GetData();
}
break;
case PT_LOGIN_RQ: //登录信息包--去数据库检验信息,给此人回复
{
STRU_USERINFO_RS su;
su.m_ntype = PT_LOGIN_RS;
STRU_USERINFO *psu = ( STRU_USERINFO *)szContent;
TCHAR *sql = _T("select UserName,UserPassWord,userRole from USERINFO") ;
list<TCHAR*> lstcolumn;
list<TCHAR*> lstValue;
TCHAR szname[_DEFAULTNUM]= {0};
TCHAR szPassword[_DEFAULTNUM]= {0};
TCHAR szRole[_DEFAULTNUM]= {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,psu->m_szUserName,-1,szname,_DEFAULTNUM);
MultiByteToWideChar(CP_ACP,0,psu->m_szPassWord,-1,szPassword,_DEFAULTNUM);
MultiByteToWideChar(CP_ACP,0,psu->m_szRole,-1,szRole,_DEFAULTNUM);
#else
strcpy_s(szname,_DEFAULTNUM,psu->m_szUserName);
strcpy_s(szPassword,_DEFAULTNUM,psu->m_szPassWord);
strcpy_s(szRole,_DEFAULTNUM,psu->m_szRole);
#endif
lstcolumn.push_back(_T("UserName"));
lstcolumn.push_back(_T("UserPassWord"));
lstcolumn.push_back(_T("userRole"));
m_ado.GetData(sql,lstcolumn,lstcolumn.size(),lstValue);
list<TCHAR*>::iterator itevalue = lstValue.begin();
while(lstValue.size() >0)
{
//如果用户不同,则找下一个用户名
if(0 == wcscmp( lstValue.back(),szname))
{
lstValue.pop_back();
if(0 == wcscmp( lstValue.back(),szPassword))
{
lstValue.pop_back();
if(0 == wcscmp( lstValue.back(),szRole))
{
//如果是教师角色,则将教师状态置为在线状态
if(0 == wcscmp(L"teacher",szRole))
{
SetTeacherState(true);
m_lteacherIp = lRevip;
}
su.m_bflag = 1;
break;
}
else
{
break;
}
}
else
{
break;
}
}
lstValue.pop_back();
lstValue.pop_back();
lstValue.pop_back();
}
//登录成功,或者失败通知
m_UDPNet->SendData(lRevip,(char*)&su,sizeof(su));
}
break;
case PT_ONLINE_RQ://上线通知包--将当前包发送给所有人
{
m_UDPNet->SendData(INADDR_BROADCAST,szContent,sizeof(STRU_BROADCASTINFO));
}
break;
case PT_OFFLINE_NTF: //下线
{
STRU_BROADCASTINFO *psb = ( STRU_BROADCASTINFO *)szContent;
//如果是教师下线了,则教师状态置为离线
if(lRevip == m_lteacherIp)
{
SetTeacherState(false);
m_lteacherIp = 0;
}
m_UDPNet->SendData(INADDR_BROADCAST,szContent,sizeof(STRU_BROADCASTINFO));
}
break;
case PT_QUICKANSWER: //抢答包--将包发给所有人
{
if(*ptype == PT_QUICKANSWER)
{
m_bbusy = false;
}
m_UDPNet->SendData(INADDR_BROADCAST,szContent,sizeof(STRU_QUICKANSWER));
}
break;
case PT_DATA: //数据bao -- 将包发给所有人
{
m_UDPNet->SendData(INADDR_BROADCAST,szContent,sizeof(STRU_DATAINFO));
}
break;
case PT_ONLINE_RS: //上线回复包 -- 将包发给指定的人
{
STRU_BROADCASTINFO *psb = ( STRU_BROADCASTINFO *)szContent;
m_UDPNet->SendData(psb->m_lTargetIp,szContent,sizeof(STRU_BROADCASTINFO));
}
break;
case PT_ANSWER_RACE://我要抢答(ip)---将信息发给服务器,服务器给所有人发送停止抢答包,(包含)个人信息(ip)
// 将状态置为空闲状态,
{
if(!m_bbusy)
{
m_bbusy = true;
//发送数据包通知所有人who 抢答成功
STRU_DATAINFO sd;
in_addr inaddr;
inaddr.S_un.S_addr = lRevip;
memcpy(sd.m_szContent,inet_ntoa(inaddr),sizeof(sd.m_szContent));
strcpy_s(sd.m_szContent,"抢答成功");
//发送停止抢答包给所有人
STRU_QUICKANSWER sq;
sq.m_bflag =0;
sq.m_lHostIp = lRevip;
m_UDPNet->SendData(INADDR_BROADCAST,(char*)&sq,sizeof(STRU_QUICKANSWER));
m_UDPNet->SendData(INADDR_BROADCAST,(char*)&sd,sizeof(STRU_DATAINFO));
}
}
break;
case PT_SUMMITWORK_RQ://提交作业请求,查看教师是否在线,在线发教师的ip,离线,发自己的IP
{
STRU_SUMMITWORK ss;
ss.m_ntype = PT_SUMMITWORK_RS;
if(GetTeacherState())
{
ss.m_lIp = m_lteacherIp;
}
else
{
ss.m_lIp = INet::GetValidIp();
}
m_UDPNet->SendData(lRevip,(char*)&ss,sizeof(STRU_SUMMITWORK));
}
break;
}
return true;
}
5.13网络部分
INet.h
#pragma once
#include "Packdef.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
class INet
{
public:
INet(){}
virtual ~INet(){}
public:
//获得本机Ip地址
static long GetValidIp()
{
in_addr addr;
char szname[20] = {0};
if(!gethostname(szname,20))
{
struct hostent* phost = gethostbyname(szname);
if(phost->h_addr_list[0] != 0)
{
addr.s_addr = *(u_long *) phost->h_addr_list[0];
return addr.s_addr;
}
}
return 0;
}
virtual bool InitNetWork() = 0;
virtual bool UnInitNetWork() = 0;
virtual bool SendData(unsigned long lSendIp,char* szbuf,int nlen) = 0;
virtual void Update(StateType ntype) = 0;
};
UDPNet.h
#include "INet.h"
#include "IKernel.h"
class UDPNet :public INet
{
public:
UDPNet(IKernel *pKernel);
virtual ~UDPNet();
public:
virtual bool InitNetWork();
virtual bool UnInitNetWork();
virtual bool SendData(unsigned long lSendIp,char* szbuf,int nlen);
virtual void Update(StateType ntype);
static unsigned _stdcall ThreadProc( void * lpvoid);
void RecvData();
bool SelectSocket(SelectType ntype,SOCKET sock);
private:
SOCKET m_sockListen;
HANDLE m_hThread;
bool m_bFlagQuit;
IKernel *m_pKernel;
};
UDPNet.cpp
#include "stdafx.h"
#include "UDPNet.h"
#include <process.h>
UDPNet::UDPNet(IKernel *pKernel)
{
m_sockListen = NULL;
m_hThread = NULL;
m_bFlagQuit = false;
m_pKernel = pKernel;
}
UDPNet::~UDPNet()
{
}
bool UDPNet::InitNetWork()
{
//1.选择店规模 -- 加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
UnInitNetWork();
return false;
}
//2.创建店。店长 -- 创建套接字socket
m_sockListen = socket(AF_INET,SOCK_DGRAM ,IPPROTO_UDP);
if(INVALID_SOCKET == m_sockListen)
{
UnInitNetWork();
return false;
}
//3.绑定信息(店名,地址,电话)-绑定(ip,端口号)bind
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr =GetValidIp();
addr.sin_port = htons(_DEFAULTPORT);
if(SOCKET_ERROR == bind(m_sockListen,(const sockaddr*)&addr,sizeof(sockaddr_in)))
{
UnInitNetWork();
return false;
}
//广播属性
bool optval = true;
setsockopt(m_sockListen,SOL_SOCKET,SO_BROADCAST,(const char*)&optval,sizeof(bool));
//接收数据--I/O select
m_bFlagQuit = true;
m_hThread = (HANDLE) _beginthreadex(NULL,0,&ThreadProc,this,0,NULL);
return true;
}
unsigned _stdcall UDPNet::ThreadProc( void * lpvoid)
{
UDPNet *pthis = (UDPNet *)lpvoid;
pthis->RecvData();
return 0;
}
bool UDPNet::SelectSocket(SelectType ntype,SOCKET sock)
{
TIMEVAL tv;
tv.tv_sec = 0;
tv.tv_usec = 100;
//1.创建集合
fd_set fdsets;
//2.清空集合
FD_ZERO(&fdsets);
//3.将socket 放入集合内
FD_SET(sock,&fdsets);
//4.将集合交给SELECT 去管理
if(ntype == ST_READ)
{
select(NULL,&fdsets,NULL,NULL,&tv);
}
else if(ntype == ST_WRITE)
{
select(NULL,NULL,&fdsets,NULL,&tv);
}
//5.检验socket 是否发生网络事件
if(!FD_ISSET(sock,&fdsets))
{
return false;
}
return true;
}
void UDPNet::RecvData()
{
char szbuf[_DEFAULTPACKEF] = {0};
sockaddr_in addr;
int nsize;
while(m_bFlagQuit)
{
nsize = sizeof(sockaddr_in);
//接收数据
if(SelectSocket(ST_READ,m_sockListen))
{
int nres = recvfrom(m_sockListen,szbuf,_DEFAULTPACKEF,0,(sockaddr*)&addr,&nsize);
if(nres > 0 )
{
//交给Kernel 处理
m_pKernel->RecvData(addr.sin_addr.s_addr,szbuf);
}
}
}
}
bool UDPNet::UnInitNetWork()
{
WSACleanup();
if(m_sockListen)
{
closesocket(m_sockListen);
m_sockListen = NULL;
}
if(m_hThread)
{
m_bFlagQuit = false;
if(WAIT_TIMEOUT == WaitForSingleObject(m_hThread,100))
{
TerminateThread(m_hThread,-1);
}
CloseHandle(m_hThread);
m_hThread = NULL;
}
return true;
}
bool UDPNet::SendData(unsigned long lSendIp,char* szbuf,int nlen)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(_DEFAULTPORT);
addr.sin_addr.S_un.S_addr = (ULONG)lSendIp;
if(sendto(m_sockListen,szbuf,nlen,0,(const sockaddr*)&addr,sizeof(addr)) <= 0)
{
return false;
}
return true;
}
void UDPNet::Update(StateType ntype)
{
switch (ntype)
{
case SY_BEGIN:
InitNetWork();
break;
case SY_END:
UnInitNetWork();
break;
default:
break;
}
}
5.13数据包部分
#ifndef _PACKDEF_H
#define _PACKDEF_H
#define _DEFAULTPORT 1234
#define _DEFAULTPACKEF 1024
#define _DEFAULTSIZE 200
#define _DEFAULTNUM 20
//执行标识
enum StateType{SY_BEGIN,SY_END};
//select 管理缓冲区的类型
enum SelectType{ST_WRITE,ST_READ};
//包类型
enum PackType{PT_REGISTER_RQ,PT_REGISTER_RS,PT_LOGIN_RQ,PT_LOGIN_RS,
PT_ONLINE_RQ, PT_ONLINE_RS,PT_OFFLINE_NTF,PT_QUICKANSWER,
PT_DATA,PT_ANSWER_RACE,PT_SUMMITWORK_RQ,PT_SUMMITWORK_RS};
//协议包
//注册包。登录包
struct STRU_USERINFO
{
STRU_USERINFO()
{
m_ntype = PT_REGISTER_RQ;
ZeroMemory(m_szUserName,_DEFAULTNUM);
ZeroMemory(m_szPassWord,_DEFAULTNUM);
ZeroMemory(m_szRole,_DEFAULTNUM);
ZeroMemory(m_szCheck,_DEFAULTNUM);
}
PackType m_ntype;
char m_szUserName[_DEFAULTNUM];
char m_szPassWord[_DEFAULTNUM];
char m_szRole[_DEFAULTNUM];
char m_szCheck[_DEFAULTNUM];
};
struct STRU_USERINFO_RS
{
STRU_USERINFO_RS()
{
m_ntype = PT_REGISTER_RS;
m_bflag = 0;
}
PackType m_ntype;
bool m_bflag;
};
//上线。xiaxian
struct STRU_BROADCASTINFO
{
STRU_BROADCASTINFO()
{
m_ntype = PT_ONLINE_RQ;
m_lHostIp = 0;
m_lTargetIp = 0;
}
PackType m_ntype;
long m_lHostIp;
long m_lTargetIp;
};
//快速抢答 -- 开始抢答,停止抢答
struct STRU_QUICKANSWER
{
STRU_QUICKANSWER()
{
m_ntype = PT_QUICKANSWER;
m_bflag = 1;
m_lHostIp = 0;
}
PackType m_ntype;
bool m_bflag;
long m_lHostIp;
};
//停止扩展包---这个是为了学生抢答完成,服务器告诉所有人停止抢答
struct STRU_QUICKANSWEREX
{
STRU_QUICKANSWEREX()
{
m_ntype = PT_QUICKANSWER;
m_bflag = 1;
m_lip = 0;
}
PackType m_ntype;
bool m_bflag;
long m_lip;
};
//数据包--发送
struct STRU_DATAINFO
{
STRU_DATAINFO()
{
m_ntype = PT_DATA;
ZeroMemory(m_szContent,_DEFAULTSIZE);
m_lhostIp = 0;
}
PackType m_ntype;
char m_szContent[_DEFAULTSIZE];
long m_lhostIp;
};
//我要抢答 --个人信息,
struct STRU_ANSWER_RACE
{
STRU_ANSWER_RACE()
{
m_ntype = PT_ANSWER_RACE;
m_lHostIp = 0;
}
PackType m_ntype;
long m_lHostIp;
};
//提交作业
struct STRU_SUMMITWORK
{
STRU_SUMMITWORK()
{
m_ntype = PT_SUMMITWORK_RQ;
m_lIp = 0;
}
PackType m_ntype;
long m_lIp;
};
#endif
5.14线程池部分
MyQueue.h
#pragma once
class CLock
{
public:
CLock()
{ InitializeCriticalSection(&m_cs);}
~CLock()
{DeleteCriticalSection(&m_cs);}
void Lock()
{EnterCriticalSection(&m_cs);}
void UnLock()
{LeaveCriticalSection(&m_cs);}
private:
CRITICAL_SECTION m_cs;
};
class CAutoLock
{
public:
CAutoLock(CLock& autolock):m_autolock(autolock)
{m_autolock.Lock();}
~CAutoLock()
{m_autolock.UnLock();}
private:
CLock& m_autolock;
};
template<class T>
class MyQueue
{
public:
MyQueue():m_lReadPos(0),m_lWritePos(0),m_lQueueLen(0)
{}
~MyQueue()
{}
bool InitQueue(int len)
{
if(len <=0)
{
return false;
}
m_lQueueLen = len;
m_pQueue = new T*[len];
for(long i = 0;i < len;i++)
{
m_pQueue[i] = NULL;
}
return true;
}
bool push(T* node)
{
CAutoLock WriteLock(m_WriteLock);
//如果当前位置不为空,直接返回
if(m_pQueue[m_lWritePos] != NULL)
{
return false;
}
m_pQueue[m_lWritePos] = node;
m_lWritePos = (m_lWritePos +1)%m_lQueueLen;
return true;
}
bool pop(T*& node)
{
CAutoLock ReadLock(m_ReadLock);
if(m_pQueue[m_lReadPos] == NULL)
{
return false;
}
node = m_pQueue[m_lReadPos];
m_pQueue[m_lReadPos] = NULL;
m_lReadPos = (m_lReadPos +1)%m_lQueueLen;
return true;
}
void UnInitQueue()
{
for(long i =0;i <m_lQueueLen;i++)
{
if(m_pQueue[i])
{
delete m_pQueue[i];
m_pQueue[i] = NULL;
}
}
delete m_pQueue;
m_pQueue = NULL;
}
private:
T** m_pQueue;
long m_lReadPos;
long m_lWritePos;
long m_lQueueLen;
CLock m_ReadLock;
CLock m_WriteLock;
};
MyThreadPool.h
#pragma once
#include <list>
#include <windows.h>
#include "MyQueue.h"
namespace MyThreadPool
{
class Itask
{
public:
Itask(){}
virtual ~Itask(){}
public:
virtual void Runitask() = 0;
};
class ThreadPool
{
public:
ThreadPool(void);
~ThreadPool(void);
//1.创建线程池
bool CreateThreadPool(long lMinThreadNum,long lMaxThreadNum,long lItaskNum);
//2.销毁线程池
void DestroyThreadPool();
//3.线程函数(执行任务)
static unsigned long _stdcall ThreadProc( void* lpParameter);
//4.投递任务
bool PushItask(Itask *);
private:
//std::queue<Itask *> m_qItask;
std::list<HANDLE> m_lstThread;
bool m_bflagQuit;
HANDLE m_hSemphore;
long m_lRunThreadNum;
long m_lCreateThreadNum;
long m_lMaxThreadNum;
//CRITICAL_SECTION m_cs;
MyQueue<Itask> m_qItask;
};
}
ThreadPool.cpp
#include "stdafx.h"
#include "ThreadPool.h"
using namespace MyThreadPool;
ThreadPool::ThreadPool(void)
{
m_bflagQuit = false;
m_hSemphore = NULL;
m_lRunThreadNum = 0;
m_lCreateThreadNum =0;
m_lMaxThreadNum = 0;
//InitializeCriticalSection(&m_cs);
}
ThreadPool::~ThreadPool(void)
{
DestroyThreadPool();
}
bool ThreadPool::CreateThreadPool(long lMinThreadNum,long lMaxThreadNum,long lItaskNum)
{
//1.校验参数
if(lMinThreadNum <=0 || lMaxThreadNum <=0 )
{
return false;
}
//2.创建线程
//创建信号量
if(!m_qItask.InitQueue(lItaskNum/5))
{
return false;
}
m_hSemphore = CreateSemaphore(NULL,0,lMaxThreadNum,NULL);
m_bflagQuit = true;
for(long i = 0;i < lMinThreadNum;i++ )
{
HANDLE hThread = CreateThread(NULL,0,&ThreadProc,this,0,NULL);
if(hThread)
{
m_lstThread.push_back(hThread);
}
}
m_lCreateThreadNum = lMinThreadNum;
m_lMaxThreadNum = lMaxThreadNum;
return true;
}
bool ThreadPool::PushItask(Itask *pItask)
{
if(!pItask)return false;
//将任务加到队列中
/*EnterCriticalSection(&m_cs);
m_qItask.push(pItask);
LeaveCriticalSection(&m_cs);
*/
while(!m_qItask.push(pItask))
{
Sleep(1);
}
//如果有空闲服务员
if(m_lRunThreadNum < m_lCreateThreadNum)
{
//释放信号量
ReleaseSemaphore(m_hSemphore,1,NULL);
}
else if( m_lCreateThreadNum < m_lMaxThreadNum )
{
//如果没有闲的服务员,但是有空地,雇佣新的服务员
HANDLE hThread = CreateThread(NULL,0,&ThreadProc,this,0,NULL);
if(hThread)
{
m_lstThread.push_back(hThread);
}
m_lCreateThreadNum++;
//释放信号量
ReleaseSemaphore(m_hSemphore,1,NULL);
}
else
{
//店满了,智能等待
}
return true;
}
unsigned long _stdcall ThreadPool::ThreadProc( void* lpParameter)
{
ThreadPool *pthis = (ThreadPool*)lpParameter;
Itask *pitask = NULL;
while(pthis->m_bflagQuit)
{
//等任务--信号量
WaitForSingleObject(pthis->m_hSemphore,INFINITE);
//将空闲状态转换为工作状态
//pthis->m_lRunThreadNum++;
InterlockedIncrement(&pthis->m_lRunThreadNum);
//取任务
while(pthis->m_bflagQuit)
{
if(pthis->m_qItask.pop(pitask))
{
pitask->Runitask();
delete pitask;
}
else
{
break;
}
}
//将工作的服务员转换为空闲状态
//pthis->m_lRunThreadNum--;
InterlockedDecrement(&pthis->m_lRunThreadNum);
}
return 0;
}
void ThreadPool::DestroyThreadPool()
{
m_bflagQuit = false;
ReleaseSemaphore(m_hSemphore,m_lCreateThreadNum,NULL);
Sleep(100);
std::list<HANDLE>::iterator iteThread;
for(iteThread = m_lstThread.begin();iteThread != m_lstThread.end();iteThread++)
{
if(WAIT_TIMEOUT == WaitForSingleObject((*iteThread),100))
{
TerminateThread(*iteThread,-1);
}
if(*iteThread)
{
CloseHandle(*iteThread);
*iteThread = NULL;
}
}
if(m_hSemphore)
{
CloseHandle(m_hSemphore);
m_hSemphore = NULL;
}
//释放空间
/*Itask *pitask = NULL;
while(!m_qItask.empty())
{
pitask = m_qItask.front();
m_qItask.pop();
delete pitask;
}
DeleteCriticalSection(&m_cs);*/
}
5.2客户端
5.2.1Ikenel部分
IKernel.h
#pragma once
#include "INet.h"
#include <list>
using namespace std;
class IKernel
{
public:
IKernel();
virtual ~IKernel();
public:
virtual bool Open(HWND hwnd) = 0;
virtual void Close() = 0;
virtual bool RecvData(long lRevip,char *szContent) = 0;
virtual bool SendData(long lSendip,char *szContent,int nsize) = 0;
public:
void Attact(INet* pNet);
void Detach(INet* pNet);
void Notify(StateType ntype);
private:
list<INet*> m_lstNet;
};
IKernel.cpp
#include "stdafx.h"
#include "IKernel.h"
IKernel::IKernel()
{
}
IKernel::~IKernel()
{
m_lstNet.clear();
}
void IKernel::Attact(INet* pNet)
{
if(pNet == NULL)return;
m_lstNet.push_back(pNet);
}
void IKernel::Detach(INet* pNet)
{
list<INet*>::iterator ite = m_lstNet.begin();
while(ite != m_lstNet.end())
{
if(*ite == pNet)
{
ite = m_lstNet.erase(ite);
break;
}
ite++;
}
}
void IKernel::Notify(StateType ntype)
{
list<INet*>::iterator ite = m_lstNet.begin();
while(ite != m_lstNet.end())
{
(*ite)->Update(ntype);
ite++;
}
}
Kernel.h
#pragma once
#include "IKernel.h"
#include "UDPNet.h"
#include "tcpnet.h"
class Kernel :public IKernel
{
public:
Kernel();
virtual ~Kernel();
public:
void SetHwnd(HWND hwnd)
{
m_hWnd = hwnd;
}
virtual bool Open(HWND hwnd);
virtual void Close();
virtual bool RecvData(long lRevip,char *szContent);
virtual bool SendData(long lSendip,char *szContent,int nsize);
// bool SendFile(long lSendip,char *szfilename,int nsize);
private:
INet *m_UDPNet;
INet *m_TcpNet;
HWND m_hWnd;
bool m_bbusy;
};
Kernel.cpp
#include "stdafx.h"
#include "Kernel.h"
#include "ElectronicResponderClientDlg.h"
Kernel::Kernel()
{
m_UDPNet = new UDPNet(this);
m_TcpNet = new TCPNet(this);
Attact(m_UDPNet);
Attact(m_TcpNet);
m_bbusy = false;
}
Kernel::~Kernel()
{
if(m_UDPNet)
{
delete m_UDPNet;
m_UDPNet = NULL;
}
if(m_TcpNet)
{
delete m_TcpNet;
m_TcpNet = NULL;
}
}
bool Kernel::Open(HWND hwnd)
{
if(NULL == hwnd)return false;
m_hWnd = hwnd;
//通知启动服务器
Notify(SY_BEGIN);
//上线通知
return true;
}
void Kernel::Close()
{
//通知结束服务器
Notify(SY_END);
}
//tcp send file
/*bool Kernel::SendFile(long lSendip,char *szfilename,int nsize)
{
if(!m_TcpNet->SendData(lSendip,szfilename,nsize))
{
return false;
}
return true;
}*/
bool Kernel::SendData(long lSendip,char *szContent,int nsize)
{
if(!m_UDPNet->SendData(lSendip,szContent,nsize))
{
return false;
}
return true;
}
bool Kernel::RecvData(long lRevip,char *szContent)
{
int *ptype = (int*)szContent;
switch (*ptype)
{
case PT_REGISTER_RS: //注册回复,提示 注册成功,失败
{
STRU_USERINFO_RS *psu = ( STRU_USERINFO_RS *)szContent;
TCHAR szbuf[10] = {0};
if(psu->m_bflag)
{
wcscpy_s(szbuf,10,L"注册成功");
}
else
{
wcscpy_s(szbuf,10,L"注册失败");
}
MessageBox(m_hWnd,szbuf,L"温馨提示",MB_OKCANCEL);
}
break;
case PT_LOGIN_RS: //登录回复,
{
STRU_USERINFO_RS *psu = ( STRU_USERINFO_RS *)szContent;
if(psu->m_bflag)
{
PostMessage(m_hWnd,UM_LOGIN,psu->m_bflag,0);
}
else
{
MessageBox(m_hWnd,L"登录失败,请重新登录",L"温馨提示",MB_OKCANCEL);
}
}
break;
case PT_ONLINE_RQ://上线请求,显示,并回复
{
long lrecvip;
STRU_BROADCASTINFO *pszbuf = (STRU_BROADCASTINFO *)szContent;
SendMessage(m_hWnd,UM_ONLINE,pszbuf->m_lHostIp,0);
if(INet::GetValidIp() == pszbuf->m_lHostIp)
{
break;
}
lrecvip = pszbuf->m_lHostIp;
STRU_BROADCASTINFO sb;
sb.m_ntype = PT_ONLINE_RS;
sb.m_lHostIp = INet::GetValidIp();
m_UDPNet->SendData(lrecvip,(char*)&sb,sizeof(STRU_BROADCASTINFO));
}
break;
case PT_ONLINE_RS://上线回复包
{
STRU_BROADCASTINFO *pszbuf = (STRU_BROADCASTINFO *)szContent;
SendMessage(m_hWnd,UM_ONLINE,pszbuf->m_lHostIp,0);
}
break;
case PT_OFFLINE_NTF://下线通知
{
STRU_BROADCASTINFO *pszbuf = (STRU_BROADCASTINFO *)szContent;
SendMessage(m_hWnd,UM_OFFLINE,pszbuf->m_lHostIp,0);
}
break;
case PT_DATA: //显示
{
STRU_DATAINFO *psd = ( STRU_DATAINFO *)szContent;
if(psd->m_lhostIp == INet::GetValidIp())
{
break;
}
SendMessage(m_hWnd,UM_DATA,(WPARAM)psd->m_szContent,psd->m_lhostIp);
}
break;
case PT_QUICKANSWER: //如果是开始抢答包和停止抢答包
{
STRU_QUICKANSWER *psq = (STRU_QUICKANSWER *)szContent;
TCHAR szbuf[100] = L"请作答:";
if(psq->m_lHostIp == INet::GetValidIp())
{
SendMessage(m_hWnd,UM_QUICKANSWER,(WPARAM)psq->m_bflag,(LPARAM)szbuf);
break;
}
SendMessage(m_hWnd,UM_QUICKANSWER,(WPARAM)psq->m_bflag,0);
}
break;
case PT_SUMMITWORK_RS: //提交作业回复,如果教师在线,回复教师ip,否则回复服务器IP
{
STRU_SUMMITWORK *pss = (STRU_SUMMITWORK *)szContent;
//连接这个Ip ,发送文件
CElectronicResponderClientDlg *pWnd = ( CElectronicResponderClientDlg *)CWnd::FromHandle(m_hWnd);
m_TcpNet->SendData(pss->m_lHostIp,pWnd->m_strFileName,MAX_PATH);
}
break;
default:
break;
}
return true;
}
5.2.2网络Inet部分
INet.h
#pragma once
#include "Packdef.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
class INet
{
public:
INet(){}
virtual ~INet(){}
public:
//获得本机Ip地址
static long GetValidIp()
{
in_addr addr;
char szname[20] = {0};
if(!gethostname(szname,20))
{
struct hostent* phost = gethostbyname(szname);
if(phost->h_addr_list[0] != 0)
{
addr.s_addr = *(u_long *) phost->h_addr_list[0];
return addr.s_addr;
}
}
return 0;
}
virtual bool InitNetWork() = 0;
virtual bool UnInitNetWork() = 0;
virtual bool SendData(long lSendIp,char* szbuf,int nlen) = 0;
virtual void Update(StateType ntype) = 0;
};
TCPNet.h
#pragma once
#include "INet.h"
#include <map>
#include "IKernel.h"
using namespace std;
class TCPNet : public INet
{
public:
TCPNet(IKernel *pKernel);
virtual ~TCPNet();
virtual bool InitNetWork();
virtual bool UnInitNetWork();
virtual bool SendData(long lRecvIp,char *szBuffer,int nSize) ;
//接收连接的线程
static unsigned _stdcall ThreadAccept( void * );
void Accept();
//接收数据
static unsigned _stdcall ThreadRecv( void * );
//接收文件
static unsigned _stdcall ThreadSendfile( void * );
virtual void Update(StateType ntype);
private:
IKernel *m_pKernel;
SOCKET m_socketServer;
HANDLE m_hThreadAccept;
HANDLE m_hEventQuit;
HANDLE m_hEventAcceptCheck;
map<long,SOCKET> m_mapIpToSocket;
map<long,SOCKET>::iterator m_itemapIpToSocket;
long m_lRecviP;
char m_szFileName[MAX_PATH];
};
TCPNet.cpp
#include "stdafx.h"
#include "TCPNet.h"
#include <process.h>
#include <afx.h>
TCPNet::TCPNet(IKernel *pKernel)
{
m_pKernel = pKernel;
}
TCPNet::~TCPNet()
{
}
void TCPNet::Update(StateType ntype)
{
switch (ntype)
{
case SY_BEGIN:
InitNetWork();
break;
case SY_END:
UnInitNetWork();
break;
default:
break;
}
}
bool TCPNet::InitNetWork()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
UnInitNetWork();
return false;
}
//2.开啦--请一个职业经理 -- 创建socket (套接字)
m_socketServer = socket(AF_INET ,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == m_socketServer)
{
UnInitNetWork();
return false;
}
//3.职业经理挂着店名 地址 --bind(IP,端口号)
sockaddr_in addrin;
addrin.sin_family = AF_INET;
addrin.sin_port = htons(_DEFAULTPORT); //端口号
addrin.sin_addr.s_addr = INet::GetValidIp();//ip地址 127.0.0.1
if(SOCKET_ERROR == bind(m_socketServer,(const sockaddr*)&addrin,sizeof(addrin)))
{
UnInitNetWork();
return false;
}
if( SOCKET_ERROR ==listen(m_socketServer,10))
{
UnInitNetWork();
return false;
}
m_hEventQuit = CreateEvent(NULL,TRUE,FALSE,NULL);
m_hEventAcceptCheck = CreateEvent(NULL,TRUE,FALSE,NULL);
m_hThreadAccept =(HANDLE) _beginthreadex(NULL,0,&ThreadAccept,this,0,NULL);
return true;
}
unsigned _stdcall TCPNet::ThreadAccept( void * lpvoid)
{
TCPNet *pthis = (TCPNet*)lpvoid;
pthis->Accept();
return 0;
}
void TCPNet::Accept()
{
while(1)
{
if(WAIT_OBJECT_0 == WaitForSingleObject(m_hEventQuit,100))
{
break;
}
SOCKET m_socketWaiter = accept(m_socketServer,NULL,NULL);
if(INVALID_SOCKET == m_socketWaiter)
{
continue;
}
//接收数据
_beginthreadex(NULL,0,&ThreadRecv,(void*)m_socketWaiter,0,0);
}
SetEvent(m_hEventAcceptCheck);
}
unsigned _stdcall TCPNet::ThreadRecv( void * lpvoid)
{
SOCKET socketClient = (SOCKET )lpvoid;
const int nSize = sizeof(STRU_File_BLOCK);
char szbuf[nSize] = {0};
char szpath[MAX_PATH] = "D:\\作业\\";
char szpathTemp[MAX_PATH] = {0};
CFile file;
int nPackSize = 0;
int nRealRecvNum = 0;
int noffset = 0;
while(1)
{
recv(socketClient,(char*)&nPackSize,sizeof(int),0);
// Sleep(1);
while(nPackSize)
{
nRealRecvNum = recv(socketClient,szbuf+noffset,nPackSize,0);
noffset += nRealRecvNum;
nPackSize -=nRealRecvNum;
}
// if(nRealRecvNum >0)
// {
STRU_File_BLOCK *p = (STRU_File_BLOCK *)szbuf;
//判断包的类型 如果是目录,则创建目录 ,如果是文件则创建文件
if(p->m_fileType == FT_FILE)
{
//创建文件
if(file.m_hFile == CFile::hFileNull)
{
//strcat_s(szpath,MAX_PATH,"\\");
strcat_s(szpath,MAX_PATH,p->m_szfileName);
TCHAR szbuf[260] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szpath,-1,szbuf,MAX_PATH);
#else
strcpy_s(szbuf,MAX_PATH,szpath);
#endif
file.Open(szbuf,CFile::modeWrite|CFile::modeCreate);
}
//向文件中写
file.Write(p->m_szfileContent,p->m_filelen);
}
//else if(p->m_fileType == F_DIR)
//{
// //创建文件夹并且将文件路径改为
// strcat_s(szpath,MAX_PATH,"\\");
// strcat_s(szpath,MAX_PATH,p->m_szfileName);
// BOOL bSuccess = CreateDirectory(szpath,NULL);
//
// //strcat_s(szpath,MAX_PATH,p->m_szfileName);
// //strcpy_s(szpathTemp,MAX_PATH,szpath);
//}
else if(p->m_fileType == FT_END)
{
file.Close();
}
}
}
return 0;
}
bool TCPNet::UnInitNetWork()
{
WSACleanup();
if(m_socketServer)
{
closesocket(m_socketServer);
m_socketServer = NULL;
}
if(m_hThreadAccept)
{
SetEvent(m_hEventQuit);
if(WAIT_OBJECT_0 != WaitForSingleObject(m_hEventAcceptCheck,100))
{
TerminateThread(m_hThreadAccept,-1);
m_hThreadAccept = NULL;
}
}
if(m_hEventQuit)
{
CloseHandle(m_hEventQuit);
m_hEventQuit = NULL;
}
if(m_hEventAcceptCheck)
{
CloseHandle(m_hEventAcceptCheck);
m_hEventAcceptCheck = NULL;
}
if(m_hThreadAccept)
{
CloseHandle(m_hThreadAccept);
m_hThreadAccept = NULL;
}
return true;
}
bool TCPNet::SendData(long lRecvIp,char *szBuffer,int nSize)
{
//判断当前是否有连接,如果有则直接发送数据,否则创建连接在发送数据
m_lRecviP = lRecvIp;
strcpy_s(m_szFileName,nSize,szBuffer);
HANDLE hThread = (HANDLE)_beginthreadex(NULL,0,&ThreadSendfile,this,0,NULL);
if(hThread)
{
CloseHandle(hThread);
hThread = NULL;
}
return true;
}
unsigned _stdcall TCPNet::ThreadSendfile( void *lpvoid )
{
TCPNet *pthis = ( TCPNet *)lpvoid;
SOCKET sock = NULL;
int nsize = sizeof(STRU_File_BLOCK);
//file.Open();
sock = socket(AF_INET ,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == sock)return false;
sockaddr_in addrin;
addrin.sin_family = AF_INET;
addrin.sin_port = htons(_DEFAULTPORT); //端口号
addrin.sin_addr.s_addr = pthis->m_lRecviP;//ip地址 127.0.0.1
if(SOCKET_ERROR == connect(sock,(const sockaddr*)&addrin,sizeof(addrin)))
{
return false;
}
STRU_File_BLOCK sf;
sf.m_fileType = FT_FILE;
//打开文件
TCHAR szbuf[260] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,pthis->m_szFileName,-1,szbuf,100);
#else
strcpy_s(szbuf,100,pthis->m_szFileName);
#endif
CFile file(szbuf,CFile::modeRead);
//获得当前路径下的文件名
char *ptemp = pthis->m_szFileName;
while(*ptemp++ != '\0');
while(*(--ptemp) != '\0');
ptemp++;
strcpy_s(sf.m_szfileName,MAX_PATH,ptemp);
while(1)
{
//读文件内容
int nRelReadNum = file.Read(sf.m_szfileContent,sizeof(sf.m_szfileContent));
if(nRelReadNum >0)
{
//发送
sf.m_filelen = nRelReadNum;
send(sock,(const char*)&nsize,sizeof(int),0);
// send(sock,(const char*)&nRelReadNum,sizeof(int),0);
send(sock,(const char*)&sf,sizeof(STRU_File_BLOCK),0);
}
else
{
break;
}
}
file.Close();
//再去发送结束包
STRU_File_BLOCK sfend;
sfend.m_fileType = FT_END;
send(sock,(char*)&nsize,sizeof(int),0);
send(sock,(char*)&sfend,sizeof(sfend),0);
closesocket(sock);
return 0;
}
UDPNet.h
#include "INet.h"
#include "IKernel.h"
class UDPNet :public INet
{
public:
UDPNet(IKernel *pKernel);
virtual ~UDPNet();
public:
virtual bool InitNetWork();
virtual bool UnInitNetWork();
virtual bool SendData(long lSendIp,char* szbuf,int nlen);
virtual void Update(StateType ntype);
static unsigned _stdcall ThreadProc( void * lpvoid);
void RecvData();
bool SelectSocket(SelectType ntype,SOCKET sock);
private:
SOCKET m_sockListen;
HANDLE m_hThread;
bool m_bFlagQuit;
IKernel *m_pKernel;
};
UDPNet.cpp
#include "stdafx.h"
#include "UDPNet.h"
#include <process.h>
UDPNet::UDPNet(IKernel *pKernel)
{
m_sockListen = NULL;
m_hThread = NULL;
m_bFlagQuit = false;
m_pKernel = pKernel;
}
UDPNet::~UDPNet()
{
}
bool UDPNet::InitNetWork()
{
//1.选择店规模 -- 加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
UnInitNetWork();
return false;
}
//2.创建店。店长 -- 创建套接字socket
m_sockListen = socket(AF_INET,SOCK_DGRAM ,IPPROTO_UDP);
if(INVALID_SOCKET == m_sockListen)
{
UnInitNetWork();
return false;
}
//3.绑定信息(店名,地址,电话)-绑定(ip,端口号)bind
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr =GetValidIp();
addr.sin_port = htons(_DEFAULTPORT);
if(SOCKET_ERROR == bind(m_sockListen,(const sockaddr*)&addr,sizeof(sockaddr_in)))
{
UnInitNetWork();
return false;
}
//广播属性
bool optval = true;
setsockopt(m_sockListen,SOL_SOCKET,SO_BROADCAST,(const char*)&optval,sizeof(bool));
//接收数据--I/O select
m_bFlagQuit = true;
m_hThread = (HANDLE) _beginthreadex(NULL,0,&ThreadProc,this,0,NULL);
return true;
}
unsigned _stdcall UDPNet::ThreadProc( void * lpvoid)
{
UDPNet *pthis = (UDPNet *)lpvoid;
pthis->RecvData();
return 0;
}
bool UDPNet::SelectSocket(SelectType ntype,SOCKET sock)
{
TIMEVAL tv;
tv.tv_sec = 0;
tv.tv_usec = 100;
//1.创建集合
fd_set fdsets;
//2.清空集合
FD_ZERO(&fdsets);
//3.将socket 放入集合内
FD_SET(sock,&fdsets);
//4.将集合交给SELECT 去管理
if(ntype == ST_READ)
{
select(NULL,&fdsets,NULL,NULL,&tv);
}
else if(ntype == ST_WRITE)
{
select(NULL,NULL,&fdsets,NULL,&tv);
}
//5.检验socket 是否发生网络事件
if(!FD_ISSET(sock,&fdsets))
{
return false;
}
return true;
}
void UDPNet::RecvData()
{
char szbuf[_DEFAULTPACKEF] = {0};
sockaddr_in addr;
int nsize;
while(m_bFlagQuit)
{
nsize = sizeof(sockaddr_in);
//接收数据
if(SelectSocket(ST_READ,m_sockListen))
{
int nres = recvfrom(m_sockListen,szbuf,_DEFAULTPACKEF,0,(sockaddr*)&addr,&nsize);
if(nres > 0 )
{
//交给Kernel 处理
m_pKernel->RecvData(addr.sin_addr.s_addr,szbuf);
}
}
}
}
bool UDPNet::UnInitNetWork()
{
WSACleanup();
if(m_sockListen)
{
closesocket(m_sockListen);
m_sockListen = NULL;
}
if(m_hThread)
{
m_bFlagQuit = false;
if(WAIT_TIMEOUT == WaitForSingleObject(m_hThread,100))
{
TerminateThread(m_hThread,-1);
}
CloseHandle(m_hThread);
m_hThread = NULL;
}
return true;
}
bool UDPNet::SendData(long lSendIp,char* szbuf,int nlen)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(_DEFAULTPORT);
addr.sin_addr.S_un.S_addr = lSendIp;
if(sendto(m_sockListen,szbuf,nlen,0,(const sockaddr*)&addr,sizeof(addr)) <= 0)
{
return false;
}
return true;
}
void UDPNet::Update(StateType ntype)
{
switch (ntype)
{
case SY_BEGIN:
InitNetWork();
break;
case SY_END:
UnInitNetWork();
break;
default:
break;
}
}
5.2.3数据包部分
Packdef.h
#ifndef _PACKDEF_H
#define _PACKDEF_H
#define _DEFAULTPORT 1234
#define _DEFAULTPACKEF 1024
#define _DEFAULTSIZE 200
#define _DEFAULTNUM 20
#define ONE_PAGE 4096
//消息类型
#define UM_LOGIN WM_USER + 1
#define UM_ONLINE WM_USER + 2
#define UM_OFFLINE WM_USER + 3
#define UM_DATA WM_USER + 4
#define UM_QUICKANSWER WM_USER + 5
//执行标识
enum StateType{SY_BEGIN,SY_END};
//select 管理缓冲区的类型
enum SelectType{ST_WRITE,ST_READ};
//包类型
enum PackType{PT_REGISTER_RQ,PT_REGISTER_RS,PT_LOGIN_RQ,PT_LOGIN_RS,
PT_ONLINE_RQ, PT_ONLINE_RS,PT_OFFLINE_NTF,PT_QUICKANSWER,
PT_DATA,PT_ANSWER_RACE,PT_SUMMITWORK_RQ,PT_SUMMITWORK_RS};
//协议包
//注册包。登录包
struct STRU_USERINFO
{
STRU_USERINFO()
{
m_ntype = PT_REGISTER_RQ;
ZeroMemory(m_szUserName,_DEFAULTNUM);
ZeroMemory(m_szPassWord,_DEFAULTNUM);
ZeroMemory(m_szRole,_DEFAULTNUM);
ZeroMemory(m_szCheck,_DEFAULTNUM);
}
PackType m_ntype;
char m_szUserName[_DEFAULTNUM];
char m_szPassWord[_DEFAULTNUM];
char m_szRole[_DEFAULTNUM];
char m_szCheck[_DEFAULTNUM];
};
//注册、登录 回复包
struct STRU_USERINFO_RS
{
STRU_USERINFO_RS()
{
m_ntype = PT_REGISTER_RS;
m_bflag = 0;
}
PackType m_ntype;
bool m_bflag;
};
//上线。xiaxian
struct STRU_BROADCASTINFO
{
STRU_BROADCASTINFO()
{
m_ntype = PT_ONLINE_RQ;
m_lHostIp = 0;
m_lTargetIp = 0;
}
PackType m_ntype;
long m_lHostIp;
long m_lTargetIp;
};
//快速抢答 -- 开始抢答,停止抢答,教师收到自己发的包不需要处理
struct STRU_QUICKANSWER
{
STRU_QUICKANSWER()
{
m_ntype = PT_QUICKANSWER;
m_bflag = 1;
m_lHostIp = 0;
}
PackType m_ntype;
bool m_bflag;
long m_lHostIp;
};
停止扩展包---这个是为了学生抢答完成,服务器告诉所有人停止抢答--这个协议包可以删除--待考虑
//struct STRU_QUICKANSWEREX
//{
// STRU_QUICKANSWEREX()
// {
// m_ntype = PT_QUICKANSWER;
// m_bflag = 1;
// m_lip = 0;
// }
// PackType m_ntype;
// bool m_bflag;
// long m_lip;
//
//};
//数据包--发送
struct STRU_DATAINFO
{
STRU_DATAINFO()
{
m_ntype = PT_DATA;
ZeroMemory(m_szContent,_DEFAULTSIZE);
m_lhostIp = 0;
}
PackType m_ntype;
char m_szContent[_DEFAULTSIZE];
long m_lhostIp;
};
//我要抢答 --个人信息,
struct STRU_ANSWER_RACE
{
STRU_ANSWER_RACE()
{
m_ntype = PT_ANSWER_RACE;
m_lHostIp = 0;
}
PackType m_ntype;
long m_lHostIp;
};
//提交作业
struct STRU_SUMMITWORK
{
STRU_SUMMITWORK()
{
m_ntype = PT_SUMMITWORK_RQ;
m_lHostIp = 0;
}
PackType m_ntype;
long m_lHostIp;
};
enum FileType{FT_FILE,FT_END};
//文件块
struct STRU_File_BLOCK
{
public:
STRU_File_BLOCK()
{
ZeroMemory(m_szfileName,MAX_PATH);
ZeroMemory(m_szfileContent,ONE_PAGE);
m_filelen = 0;
//m_fileType = F_UNKOWN;
}
public:
char m_szfileName[MAX_PATH];
char m_szfileContent[ONE_PAGE];
int m_filelen;
FileType m_fileType;
};
#endif
5.2.4main部分
CElectronicResponderClientDlg.h
// ElectronicResponderClientDlg.h : 头文件
//
#pragma once
#include "resource.h"
#include "afxwin.h"
// CElectronicResponderClientDlg 对话框
class CElectronicResponderClientDlg : public CDialogEx
{
// 构造
public:
CElectronicResponderClientDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_DLG_STUDENT };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg LRESULT OnLineMsg(WPARAM wparam,LPARAM lparam);
afx_msg LRESULT OffLineMsg(WPARAM wparam,LPARAM lparam);
afx_msg LRESULT ShowDataMsg(WPARAM wparam,LPARAM lparam);
afx_msg LRESULT QuickAnswerMsg(WPARAM wparam,LPARAM lparam);
DECLARE_MESSAGE_MAP()
public:
CListBox m_lstData;
CEdit m_lstSend;
CListBox m_lstIp;
char m_strFileName[MAX_PATH];
afx_msg void OnBnClickedButton4();
afx_msg void OnDestroy();
afx_msg void OnBnClickedButton1();
afx_msg void OnBnClickedButSumitwork();
};
CElectronicResponderClientDlg.cpp
// ElectronicResponderClientDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "ElectronicResponderClient.h"
#include "ElectronicResponderClientDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CElectronicResponderClientDlg 对话框
CElectronicResponderClientDlg::CElectronicResponderClientDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CElectronicResponderClientDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CElectronicResponderClientDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_lstData);
DDX_Control(pDX, IDC_EDIT1, m_lstSend);
DDX_Control(pDX, IDC_LIST2, m_lstIp);
}
BEGIN_MESSAGE_MAP(CElectronicResponderClientDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(UM_ONLINE,&CElectronicResponderClientDlg::OnLineMsg)
ON_MESSAGE(UM_OFFLINE,&CElectronicResponderClientDlg::OffLineMsg)
ON_MESSAGE(UM_DATA,&CElectronicResponderClientDlg::ShowDataMsg)
ON_MESSAGE(UM_QUICKANSWER,&CElectronicResponderClientDlg::QuickAnswerMsg)
ON_BN_CLICKED(IDC_BUT_SEND, &CElectronicResponderClientDlg::OnBnClickedButton4)
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_BUT_QUICKANSWER, &CElectronicResponderClientDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUT_SUMITWORK, &CElectronicResponderClientDlg::OnBnClickedButSumitwork)
END_MESSAGE_MAP()
// CElectronicResponderClientDlg 消息处理程序
BOOL CElectronicResponderClientDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
GetDlgItem(IDC_BUT_SEND)->EnableWindow(0);
GetDlgItem(IDC_BUT_QUICKANSWER)->EnableWindow(0);
((Kernel*)theApp.m_pKernel)->SetHwnd( theApp.m_pMainWnd->m_hWnd);
// TODO: 在此添加额外的初始化代码
//广播上线
STRU_BROADCASTINFO sb;
sb.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sb,sizeof(STRU_BROADCASTINFO));
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CElectronicResponderClientDlg::OnDestroy()
{
CDialogEx::OnDestroy();
// TODO: 在此处添加消息处理程序代码
STRU_BROADCASTINFO sb;
sb.m_ntype = PT_OFFLINE_NTF;
sb.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sb,sizeof(STRU_BROADCASTINFO));
}
//上线
LRESULT CElectronicResponderClientDlg::OnLineMsg(WPARAM wparam,LPARAM lparam)
{
//将Ip 转为字符串
in_addr addr;
addr.S_un.S_addr = wparam;
char *szip = inet_ntoa(addr);
TCHAR szbuf[100] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#else
strcpy_s(szbuf,100,szip);
#endif
m_lstIp.AddString(szbuf);
return 0;
}
//下线
LRESULT CElectronicResponderClientDlg::OffLineMsg(WPARAM wparam,LPARAM lparam)
{
//将Ip 转为字符串
in_addr addr;
addr.S_un.S_addr = wparam;
char *szip = inet_ntoa(addr);
CString strIP;
TCHAR szbuf[100] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#else
strcpy_s(szbuf,100,szip);
#endif
for(int i = 0; i < m_lstIp.GetCount();i++ )
{
m_lstIp.GetText(i,strIP);
if(!_tcscmp(szbuf,strIP))
{
m_lstIp.DeleteString(i);
break;
}
}
return 0;
}
//显示数据
LRESULT CElectronicResponderClientDlg::ShowDataMsg(WPARAM wparam,LPARAM lparam)
{
char *pContent =(char*)wparam;
//IP :
in_addr addr;
addr.S_un.S_addr = lparam;
char *szip = inet_ntoa(addr);
CString strIP;
TCHAR szbufIP[100] = {0};
TCHAR szContent[_DEFAULTSIZE] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szip,-1,szbufIP,100);
MultiByteToWideChar(CP_ACP,0,pContent,-1,szContent,_DEFAULTSIZE);
#else
strcpy_s(szbufIP,100,szip);
strcpy_s(szContent,_DEFAULTSIZE,pContent);
#endif
m_lstData.AddString(szbufIP);
m_lstData.AddString(szContent);
return 0;
}
//是否可以抢答
LRESULT CElectronicResponderClientDlg::QuickAnswerMsg(WPARAM wparam,LPARAM lparam)
{
//如果lparam有值,则代表已经抢到,可以开始答题
TCHAR *szbuf= (TCHAR*)lparam;
if(szbuf)
{
m_lstData.AddString(_T("系统提示:请您开始抢答"));
return 0;
}
//如果wparam --为1 则按钮可用 否则不可用
GetDlgItem(IDC_BUT_SEND)->EnableWindow(wparam);
GetDlgItem(IDC_BUT_QUICKANSWER)->EnableWindow(wparam);
if(wparam)
{
m_lstData.AddString(_T("系统提示:开始抢答"));
}
return 0;
}
//我要抢答
void CElectronicResponderClientDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
STRU_ANSWER_RACE sa;
sa.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sa,sizeof(STRU_ANSWER_RACE));
}
//发送数据
void CElectronicResponderClientDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
m_lstSend.GetWindowText(str);
STRU_DATAINFO sd;
sd.m_lhostIp =INet::GetValidIp();
TCHAR szbuf[100] = {0};
#ifdef _UNICODE
WideCharToMultiByte(CP_ACP,0,str,-1,sd.m_szContent,_DEFAULTSIZE,0,0);
#else
strcpy_s(sd.m_szContent,_DEFAULTSIZE,str);
#endif
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sd,sizeof(STRU_DATAINFO));
m_lstData.AddString(_T("我说:"));
m_lstData.AddString(str);
}
void CElectronicResponderClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CElectronicResponderClientDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CElectronicResponderClientDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//提交作业
void CElectronicResponderClientDlg::OnBnClickedButSumitwork()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(TRUE);
if(IDOK == dlg.DoModal())
{
#ifdef _UNICODE
WideCharToMultiByte(CP_ACP,0,dlg.GetPathName(),-1,m_strFileName,MAX_PATH,0,0);
#else
strcpy_s(m_strFileName,MAX_PATH,dlg.GetPathName());
#endif
STRU_SUMMITWORK ss;
ss.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&ss,sizeof(STRU_SUMMITWORK));
}
}
CMyLogin.h
#pragma once
// CMyLogin 对话框
class CMyLogin : public CDialogEx
{
DECLARE_DYNAMIC(CMyLogin)
public:
CMyLogin(CWnd* pParent = NULL); // 标准构造函数
virtual ~CMyLogin();
// 对话框数据
enum { IDD = IDD_DLG_LOGIN };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
afx_msg LRESULT OnLoginMsg(WPARAM wparam,LPARAM lparam);
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
afx_msg void OnBnClickedButton1();
CString m_edtUserName;
CString m_edtPassWord;
CString m_comRole;
CString m_edtCheck;
afx_msg void OnBnClickedButton2();
};
CMyLogin.cpp
// MyLogin.cpp : 实现文件
//
#include "stdafx.h"
#include "ElectronicResponderClient.h"
#include "MyLogin.h"
#include "afxdialogex.h"
#include "Packdef.h"
#include "ElectronicResponderClientDlg.h"
#include "MyTeacher.h"
// CMyLogin 对话框
IMPLEMENT_DYNAMIC(CMyLogin, CDialogEx)
CMyLogin::CMyLogin(CWnd* pParent /*=NULL*/)
: CDialogEx(CMyLogin::IDD, pParent)
, m_edtUserName(_T(""))
, m_edtPassWord(_T(""))
, m_comRole(_T(""))
, m_edtCheck(_T(""))
{
theApp.m_pKernel = new Kernel;
}
CMyLogin::~CMyLogin()
{
if(theApp.m_pKernel)
{
delete theApp.m_pKernel;
theApp.m_pKernel = NULL;
}
}
void CMyLogin::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDT_USERNAME, m_edtUserName);
DDX_Text(pDX, IDC_EDT_PASSWORD, m_edtPassWord);
DDX_CBString(pDX, IDC_COM_ROLE, m_comRole);
DDX_Text(pDX, IDC_EDT_CHECK, m_edtCheck);
}
BEGIN_MESSAGE_MAP(CMyLogin, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON1, &CMyLogin::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CMyLogin::OnBnClickedButton2)
ON_MESSAGE(UM_LOGIN,&CMyLogin::OnLoginMsg)
END_MESSAGE_MAP()
// CMyLogin 消息处理程序
BOOL CMyLogin::OnInitDialog()
{
CDialogEx::OnInitDialog();
GetDlgItem(IDC_BUTTON2)->EnableWindow(0);
theApp.m_pKernel->Open(m_hWnd);
return TRUE; // return TRUE unless you set the focus to a control
}
LRESULT CMyLogin::OnLoginMsg(WPARAM wparam,LPARAM lparam)
{
//判断当前身份,
if(m_comRole == "student")
{
CElectronicResponderClientDlg dlg;
theApp.m_pMainWnd = &dlg;
CDialogEx::OnOK();
dlg.DoModal();
}
else
{
CMyTeacher dlg;
theApp.m_pMainWnd = &dlg;
// ((Kernel*)theApp.m_pKernel)->SetHwnd( theApp.m_pMainWnd->m_hWnd);
CDialogEx::OnOK();
dlg.DoModal();
}
return 0;
}
void CMyLogin::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
//获得用户信息
UpdateData(TRUE);
STRU_USERINFO su;
#ifdef _UNICODE
WideCharToMultiByte(CP_ACP,0,m_edtUserName,-1,su.m_szUserName,_DEFAULTNUM,0,0);
WideCharToMultiByte(CP_ACP,0,m_edtPassWord,-1,su.m_szPassWord,_DEFAULTNUM,0,0);
WideCharToMultiByte(CP_ACP,0,m_comRole,-1,su.m_szRole,_DEFAULTNUM,0,0);
#else
strcpy_s(su.m_szUserName,_DEFAULTNUM,m_edtUserName);
strcpy_s(su.m_szPassWord,_DEFAULTNUM,m_edtPassWord);
strcpy_s(su.m_szRole,_DEFAULTNUM,m_comRole);
#endif
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&su,sizeof(STRU_USERINFO));
}
void CMyLogin::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
//获得用户信息
UpdateData(TRUE);
STRU_USERINFO su;
su.m_ntype = PT_LOGIN_RQ;
#ifdef _UNICODE
WideCharToMultiByte(CP_ACP,0,m_edtUserName,-1,su.m_szUserName,_DEFAULTNUM,0,0);
WideCharToMultiByte(CP_ACP,0,m_edtPassWord,-1,su.m_szPassWord,_DEFAULTNUM,0,0);
WideCharToMultiByte(CP_ACP,0,m_comRole,-1,su.m_szRole,_DEFAULTNUM,0,0);
#else
strcpy_s(su.m_szUserName,_DEFAULTNUM,m_edtUserName);
strcpy_s(su.m_szPassWord,_DEFAULTNUM,m_edtPassWord);
strcpy_s(su.m_szRole,_DEFAULTNUM,m_comRole);
#endif
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&su,sizeof(STRU_USERINFO));
}
CMyTeacher.h
#pragma once
#include "afxwin.h"
// CMyTeacher 对话框
class CMyTeacher : public CDialogEx
{
DECLARE_DYNAMIC(CMyTeacher)
public:
CMyTeacher(CWnd* pParent = NULL); // 标准构造函数
virtual ~CMyTeacher();
// 对话框数据
enum { IDD = IDD_DLG_TEACHER };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
afx_msg LRESULT OnLineMsg(WPARAM wparam,LPARAM lparam);
afx_msg LRESULT OffLineMsg(WPARAM wparam,LPARAM lparam);
afx_msg LRESULT ShowDataMsg(WPARAM wparam,LPARAM lparam);
afx_msg void OnBnClickedButton1();//开始抢答
afx_msg void OnBnClickedButton3();//停止抢答
afx_msg void OnBnClickedButton4();//发送数据
virtual BOOL OnInitDialog();
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()
public:
CListBox m_lstIp;
CListBox m_lstData;
CEdit m_edtSend;
};
CMyTeacher.cpp
// MyTeacher.cpp : 实现文件
//
#include "stdafx.h"
#include "ElectronicResponderClient.h"
#include "MyTeacher.h"
#include "afxdialogex.h"
#include "ElectronicResponderClient.h"
// CMyTeacher 对话框
IMPLEMENT_DYNAMIC(CMyTeacher, CDialogEx)
CMyTeacher::CMyTeacher(CWnd* pParent /*=NULL*/)
: CDialogEx(CMyTeacher::IDD, pParent)
{
}
CMyTeacher::~CMyTeacher()
{
}
void CMyTeacher::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST2, m_lstIp);
DDX_Control(pDX, IDC_EDIT1, m_edtSend);
DDX_Control(pDX, IDC_LIST1, m_lstData);
}
BEGIN_MESSAGE_MAP(CMyTeacher, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON1, &CMyTeacher::OnBnClickedButton1)
ON_WM_DESTROY()
ON_MESSAGE(UM_ONLINE,&CMyTeacher::OnLineMsg)
ON_MESSAGE(UM_OFFLINE,&CMyTeacher::OffLineMsg)
ON_MESSAGE(UM_DATA,&CMyTeacher::ShowDataMsg)
ON_BN_CLICKED(IDC_BUTTON3, &CMyTeacher::OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON4, &CMyTeacher::OnBnClickedButton4)
END_MESSAGE_MAP()
// CMyTeacher 消息处理程序
//初始化
BOOL CMyTeacher::OnInitDialog()
{
CDialogEx::OnInitDialog();
((Kernel*)theApp.m_pKernel)->SetHwnd( theApp.m_pMainWnd->m_hWnd);
//广播上线
STRU_BROADCASTINFO sb;
sb.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sb,sizeof(STRU_BROADCASTINFO));
// GetDlgItem(IDC_BUTTON3)->EnableWindow(0);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
//结束
void CMyTeacher::OnDestroy()
{
CDialogEx::OnDestroy();
STRU_BROADCASTINFO sb;
sb.m_ntype = PT_OFFLINE_NTF;
sb.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sb,sizeof(STRU_BROADCASTINFO));
// TODO: 在此处添加消息处理程序代码
}
//开始抢答
void CMyTeacher::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
STRU_QUICKANSWER sq;
sq.m_bflag = 1;
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sq,sizeof(STRU_QUICKANSWER));
//
}
//停止抢答
void CMyTeacher::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
//停止抢答
STRU_QUICKANSWER sq;
sq.m_bflag = 0;
sq.m_lHostIp = INet::GetValidIp();
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sq,sizeof(STRU_QUICKANSWER));
}
//上线
LRESULT CMyTeacher::OnLineMsg(WPARAM wparam,LPARAM lparam)
{
//将Ip 转为字符串
in_addr addr;
addr.S_un.S_addr = wparam;
char *szip = inet_ntoa(addr);
TCHAR szbuf[100] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#else
strcpy_s(szbuf,100,szip);
#endif
m_lstIp.AddString(szbuf);
return 0;
}
//下线
LRESULT CMyTeacher::OffLineMsg(WPARAM wparam,LPARAM lparam)
{
//将Ip 转为字符串
in_addr addr;
addr.S_un.S_addr = wparam;
char *szip = inet_ntoa(addr);
CString strIP;
TCHAR szbuf[100] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#else
strcpy_s(szbuf,100,szip);
#endif
for(int i = 0; i < m_lstIp.GetCount();i++ )
{
m_lstIp.GetText(i,strIP);
if(!_tcscmp(szbuf,strIP))
{
m_lstIp.DeleteString(i);
break;
}
}
return 0;
}
//显示数据
LRESULT CMyTeacher::ShowDataMsg(WPARAM wparam,LPARAM lparam)
{
char *pContent =(char*)wparam;
//IP :
in_addr addr;
addr.S_un.S_addr = lparam;
char *szip = inet_ntoa(addr);
CString strIP;
TCHAR szbufIP[100] = {0};
TCHAR szContent[_DEFAULTSIZE] = {0};
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP,0,szip,-1,szbufIP,100);
MultiByteToWideChar(CP_ACP,0,pContent,-1,szContent,_DEFAULTSIZE);
#else
strcpy_s(szbufIP,100,szip);
strcpy_s(szContent,_DEFAULTSIZE,pContent);
#endif
m_lstData.AddString(szbufIP);
m_lstData.AddString(szContent);
return 0;
}
//发送数据
void CMyTeacher::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
m_edtSend.GetWindowText(str);
STRU_DATAINFO sd;
sd.m_lhostIp =INet::GetValidIp();
TCHAR szbuf[100] = {0};
#ifdef _UNICODE
WideCharToMultiByte(CP_ACP,0,str,-1,sd.m_szContent,_DEFAULTSIZE,0,0);
#else
strcpy_s(sd.m_szContent,_DEFAULTSIZE,str);
#endif
theApp.m_pKernel->SendData(inet_addr("192.168.1.252"),(char*)&sd,sizeof(STRU_DATAINFO));
m_lstData.AddString(_T("我说:"));
m_lstData.AddString(str);
}