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);#endifm_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 _UNICODEMultiByteToWideChar(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);
#elsestrcpy_s(szname,_DEFAULTNUM,psu->m_szUserName); strcpy_s(szPassword,_DEFAULTNUM,psu->m_szPassWord); strcpy_s(szRole,_DEFAULTNUM,psu->m_szRole);
#endiflstValue.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 _UNICODEMultiByteToWideChar(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);
#elsestrcpy_s(szname,_DEFAULTNUM,psu->m_szUserName); strcpy_s(szPassword,_DEFAULTNUM,psu->m_szPassWord); strcpy_s(szRole,_DEFAULTNUM,psu->m_szRole);
#endiflstcolumn.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 onceclass 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.1if(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 _UNICODEMultiByteToWideChar(CP_ACP,0,szpath,-1,szbuf,MAX_PATH);
#elsestrcpy_s(szbuf,MAX_PATH,szpath);
#endiffile.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.1if(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 _UNICODEMultiByteToWideChar(CP_ACP,0,pthis->m_szFileName,-1,szbuf,100);
#elsestrcpy_s(szbuf,100,pthis->m_szFileName);
#endifCFile 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 _UNICODEMultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#elsestrcpy_s(szbuf,100,szip);
#endifm_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 _UNICODEMultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#elsestrcpy_s(szbuf,100,szip);
#endiffor(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 _UNICODEMultiByteToWideChar(CP_ACP,0,szip,-1,szbufIP,100);MultiByteToWideChar(CP_ACP,0,pContent,-1,szContent,_DEFAULTSIZE);
#elsestrcpy_s(szbufIP,100,szip);strcpy_s(szContent,_DEFAULTSIZE,pContent);
#endifm_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 _UNICODEWideCharToMultiByte(CP_ACP,0,str,-1,sd.m_szContent,_DEFAULTSIZE,0,0);
#elsestrcpy_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 _UNICODEWideCharToMultiByte(CP_ACP,0,dlg.GetPathName(),-1,m_strFileName,MAX_PATH,0,0);
#elsestrcpy_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 _UNICODEWideCharToMultiByte(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);
#elsestrcpy_s(su.m_szUserName,_DEFAULTNUM,m_edtUserName);strcpy_s(su.m_szPassWord,_DEFAULTNUM,m_edtPassWord);strcpy_s(su.m_szRole,_DEFAULTNUM,m_comRole);
#endiftheApp.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 _UNICODEWideCharToMultiByte(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);
#elsestrcpy_s(su.m_szUserName,_DEFAULTNUM,m_edtUserName);strcpy_s(su.m_szPassWord,_DEFAULTNUM,m_edtPassWord);strcpy_s(su.m_szRole,_DEFAULTNUM,m_comRole);
#endiftheApp.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 _UNICODEMultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#elsestrcpy_s(szbuf,100,szip);
#endifm_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 _UNICODEMultiByteToWideChar(CP_ACP,0,szip,-1,szbuf,100);
#elsestrcpy_s(szbuf,100,szip);
#endiffor(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 _UNICODEMultiByteToWideChar(CP_ACP,0,szip,-1,szbufIP,100);MultiByteToWideChar(CP_ACP,0,pContent,-1,szContent,_DEFAULTSIZE);
#elsestrcpy_s(szbufIP,100,szip);strcpy_s(szContent,_DEFAULTSIZE,pContent);
#endifm_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 _UNICODEWideCharToMultiByte(CP_ACP,0,str,-1,sd.m_szContent,_DEFAULTSIZE,0,0);
#elsestrcpy_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);
}

基于MFC实现的网络教室相关推荐

  1. 基于MFC+WinpCap的网络嗅探器(sniffer)

    运行效果: 这篇blog里面对于开发写的很详细了 https://blog.csdn.net/litingli/article/details/5950962 源码下载:https://downloa ...

  2. 网络编程-基于MFC的仿QQ聊天室-2020

    基于MFC的仿QQ聊天室(2020) 有幸学习过网络编程的一些知识,出于对编程的热爱,把曾经的一次简单实践编程作业进行了自定义的完成. 编程所需: 编程工具为VS 2010,需要掌握MFC的基本操作以 ...

  3. 基于MFC的socket编程(异步非阻塞通信)

    对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手.许多概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等,初学者往往迷惑不清,只知其 ...

  4. C++基于MFC课程设计——学习公社

    学习公社课程设计 学习公社 一.系统使用展示 二.系统主要结构 1.系统功能介绍 2.数据库表的设计 用户表 资源表 3.MySQL数据库与vs连接 三.主要源代码及分析: ==VS和MySQL的连接 ...

  5. 基于物联网技术的智慧教室重点设备监控系统设计与实现

    完整文件下载,关注:码视野,回复关键字:2001 一.引言 1.1 研究背景和意义 随着新技术的广泛应用,物联网作为新一代信息技术,在各个领域中的应用也越来越广泛.在教育领域中,物联网已经成为教育发展 ...

  6. 基于MFC的餐饮管理系统(C++实现 后附源码)

    餐饮管理系统实现功能: ①利用MFC的向导,创建基于对话框的应用程序,添加按钮.编辑框等控件: ②用文件保存餐桌信息,包括餐桌编号.座位数.状态(0:空闲:1:已订:2:就餐):可以按座位数和餐桌状态 ...

  7. 用C#实现基于TCP协议的网络通讯

    TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...

  8. 基于交换技术的网络中,全双工主要运行在?( 内有答案与详解)

    基于交换技术的网络中,全双工主要运行在?( ) A. 站点与站点之间 B. 交换机与服务器之间 C. 站点与服务器之间 D. 站点与交换机之间 答案: b 网站就是站点的意思,交换机实际是与数据打交道 ...

  9. 《中国人工智能学会通讯》——第3章 3.1基于深度学习的网络表示研究进展

    第3章 3.1基于深度学习的网络表示研究进展 网络结构在现实世界中无处不在(如航线网络.通信网络.论文引用网络.世界万维网和社交网络等),在此基础之上的应用和研究问题受到了学术界和工业界的广泛关注,这 ...

最新文章

  1. 【从零开始的ROS四轴机械臂控制】(三) - 为机械臂添加摄像头和夹爪、解决gazebo模型抖动、使用gazebo建立sdf模型
  2. ti的硬件时钟和系统时钟同步
  3. 《Imperfect C++中文版》——2.1 对象生命周期
  4. 11、如何开启慢日志查询?
  5. springboot默认数据源如何设置连接数_Spring Boot系列之配置数据库连接池
  6. 猜数(二分、线段树)
  7. ios点击大头针气泡不弹出_iOS高德地图之自定义大头针and泡泡view
  8. testng使用DataProvider+Excel实现DDT
  9. 【gateway系列】手把手教你gateway整合nacos注册中心
  10. 公司打印机的安装和使用
  11. n维椭球体积公式_加速度计 椭球校准 (最小二乘法 椭球拟合)
  12. linq To Xml 用法简介
  13. 80psi等于多少kpa_压力单位PSI与kpa换算
  14. LSMW 批量更改BOM 成本核算标识相关标识 特殊处理
  15. PyCharm 和 VScode 我更适合用哪个?
  16. 任意文件下载(读取)
  17. IP地址定位功能在网站上的应用
  18. 大四 Java开发实习近一年 记录(每6至12月更新一次)
  19. 多线程 4——线程通信、线程池、定时器
  20. matlab:基本操作与矩阵输入

热门文章

  1. leaflet 示例教程100+ 目录
  2. 99%的面试官都会问的一个问题,这样答就能轻松过关
  3. mysql中间件研究( Atlas,cobar,TDDL,mycat,heisenberg,Oceanus,vitess )
  4. [fsevents@^2.1.2] optional install error: Package require os(darwin) not compatible with your platfo
  5. react实现贪吃蛇
  6. Appium元素定位和案例(有注释)
  7. 劲爆!java开发哪些公司比较好
  8. Android开发——Kotlin语言
  9. chinese linux
  10. 如何快速将Android Support Library项目升级到AndroidX