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); }